[英]User Control Dependency Property Updating Multiple control values
I have a feeling my base issue with this problem is binding some internal properties.我觉得我对这个问题的基本问题是绑定一些内部属性。
In the usercontrol I have a rectangle that contains a linear gradient.在用户控件中,我有一个包含线性渐变的矩形。 I created a dependency property to be able to give a value (0 to 1) to specify where the gradient line should be in the rectangle.
我创建了一个依赖属性,以便能够给出一个值(0 到 1)来指定渐变线在矩形中的位置。 To adjust this value dynamically I connected a slider to it for testing.
为了动态调整这个值,我连接了一个 slider 到它进行测试。
I also added some feedback textblocks to let me know how some of the data is flowing and if it is flowing.我还添加了一些反馈文本块,让我知道一些数据是如何流动的,以及它是否在流动。 And from that I can tell my binding to my GradientStops.offset values from my user control properties are not working.
从那我可以告诉我从我的用户控件属性绑定到我的 GradientStops.offset 值不起作用。 How do I get the user control to update the rectangle gradient by just changing RectangleLevel.RectLevel value?
如何让用户控件通过更改 RectangleLevel.RectLevel 值来更新矩形渐变?
USERCONTROL XAML USERCONTROL XAML
<UserControl x:Class="RectDynamicGradient.RectangleLevel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RectDynamicGradient"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
d:DesignHeight="180" d:DesignWidth="80">
<Grid>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Cyan" Offset="{Binding Gradient_top_color, diag:PresentationTraceSources.TraceLevel=High}"/>
<GradientStop Color="Black" Offset="{Binding Gradient_bottom_color, diag:PresentationTraceSources.TraceLevel=High}"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
USERCONTROL CODE用户控制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for RectangleLevel.xaml
/// </summary>
public partial class RectangleLevel : UserControl
{
public double Gradient_bottom_color { get; set; }
public double Gradient_top_color { get; set; }
public double RectLevel
{
get { return (double)GetValue(RectLevelProperty); }
set { SetValue(RectLevelProperty, value); }
}
// Using a DependencyProperty as the backing store for RectLevel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RectLevelProperty =
DependencyProperty.Register("RectLevel", typeof(double), typeof(RectangleLevel), new FrameworkPropertyMetadata(0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(ChangeLevel),
new CoerceValueCallback(CoerceLevel)),
new ValidateValueCallback(ValidateLevel));
public static void ChangeLevel(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RectangleLevel t = d as RectangleLevel;
t.UpdateGradientStops((double)e.NewValue);
}
public static object CoerceLevel(DependencyObject d, object value)
{
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
return lvl;
}
}
if (value is double valdouble)
{
return valdouble;
}
throw new Exception();
}
public static bool ValidateLevel(object value)
{
double? level = 0;
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
level = lvl;
}
}
if (value is double valdouble)
{
level = valdouble;
}
if (level.HasValue && level >= 0 && level <= 1)
return true;
else
return false;
}
public RectangleLevel()
{
InitializeComponent();
this.DataContext = this;
}
private void UpdateGradientStops(double level)
{
double scale = 0;
if (level < .5)
{
scale = level;
}
else if (level >= .5)
{
scale = 1 - level;
}
Gradient_top_color = level;
Gradient_bottom_color = level + (level * .1 * scale);
}
}
}
MAINWINDOW XAML主窗口 XAML
<Window x:Class="RectDynamicGradient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RectDynamicGradient"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="TheSlider" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" LargeChange="0.1" Maximum="1" SmallChange="0.01" >
<Slider.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="2"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="2"/>
</TransformGroup>
</Slider.RenderTransform>
</Slider>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<TextBlock Text="SliderValue:"/>
<TextBlock HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<TextBlock Text="UC LEVEL Value:"/>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=MyUserControl, Path=RectLevel}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
<local:RectangleLevel x:Name="MyUserControl" Width="80" VerticalAlignment="Stretch" RectLevel="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
</Grid>
</Window>
MAINWINDOW CODE主窗口代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
EDIT编辑
After feedback from the comments the improvments were made.在收到评论反馈后,进行了改进。
DataContext removed this and added relative source to user control (named "uc") DataContext 删除了这个并将相对源添加到用户控件(名为“uc”)
Changed NotifyProperties into DependencyProperties将 NotifyProperties 更改为 DependencyProperties
Replaced DataContext替换数据上下文
<GradientStop Color="Cyan" Offset="{Binding ElementName=uc, Path=GradientFilledColor}"/>
Updated Dependency Properties更新的依赖属性
/// <summary>
/// Property to change the GradientEmptyColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientEmptyColor
{
get { return (double)GetValue(GradientEmptyColorProperty); }
set { SetValue(GradientEmptyColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientEmptyColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientEmptyColorProperty =
DependencyProperty.Register("GradientEmptyColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the GradientFilledColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientFilledColor
{
get { return (double)GetValue(GradientFilledColorProperty); }
set { SetValue(GradientFilledColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientFilledColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientFilledColorProperty =
DependencyProperty.Register("GradientFilledColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
The DependencyProperty provides the same capability as the INotifyProperty PropertChanged event (uses a different mechanism though). DependencyProperty 提供与 INotifyProperty PropertChanged 事件相同的功能(但使用不同的机制)。 As mentioned by @Clemens setting datacontext to self is a terrible idea and would break linking when the user control is used through-out the project and datacontext is set to something else.
正如@Clemens 所提到的,将 datacontext 设置为 self 是一个糟糕的想法,并且当用户控件在整个项目中使用并且 datacontext 设置为其他内容时会中断链接。
I am still open to suggestions that help support good practices and learning.我仍然愿意接受有助于支持良好实践和学习的建议。
UPDATED CODE更新代码
USERCONTROL XAML USERCONTROL XAML
<UserControl x:Class="RectDynamicGradient.RectangleLevel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RectDynamicGradient"
mc:Ignorable="d"
d:DesignHeight="180" d:DesignWidth="80" x:Name="uc">
<Grid>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".5,1" EndPoint=".5,0">
<GradientStop Color="Cyan" Offset="{Binding ElementName=uc, Path=GradientFilledColor}"/>
<GradientStop Color="Black" Offset="{Binding ElementName=uc, Path=GradientEmptyColor}"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
USERCONTROL CODE用户控制代码
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for RectangleLevel.xaml
/// </summary>
public partial class RectangleLevel : UserControl
{
/// <summary>
/// Property to change the GradientEmptyColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientEmptyColor
{
get { return (double)GetValue(GradientEmptyColorProperty); }
set { SetValue(GradientEmptyColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientEmptyColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientEmptyColorProperty =
DependencyProperty.Register("GradientEmptyColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the GradientFilledColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientFilledColor
{
get { return (double)GetValue(GradientFilledColorProperty); }
set { SetValue(GradientFilledColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientFilledColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientFilledColorProperty =
DependencyProperty.Register("GradientFilledColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the level (GradientStop offsets) of the rectangle by interfacing with the dependency property.
/// </summary>
public double RectLevel
{
get { return (double)GetValue(RectLevelProperty); }
set { SetValue(RectLevelProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for RectLevel. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty RectLevelProperty =
DependencyProperty.Register("RectLevel", typeof(double), typeof(RectangleLevel), new FrameworkPropertyMetadata(0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(ChangeLevel),
new CoerceValueCallback(CoerceLevel)),
new ValidateValueCallback(ValidateLevel));
/// <summary>
/// PropertyChangedCallback for DependencyProperty RectLevelProperty.
/// </summary>
/// <param name="d">Dependency object causing the event.</param>
/// <param name="e">Change event arguments.</param>
public static void ChangeLevel(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RectangleLevel t = d as RectangleLevel;
t.UpdateGradientStops((double)e.NewValue);
}
/// <summary>
/// CoerceValueCallback for DependencyProperty RectLevelProperty.
/// </summary>
/// <param name="d">Dependency object causing the event.</param>
/// <param name="value">Value being sent to RectLevelProperty be coerced.</param>
/// <returns></returns>
public static object CoerceLevel(DependencyObject d, object value)
{
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
return lvl;
}
}
if (value is double valdouble)
{
return valdouble;
}
throw new Exception();
}
/// <summary>
/// ValidateValueCallback for DependencyProperty RectLevelProperty
/// </summary>
/// <param name="value">Value being sent to RectLevelProperty be validated.</param>
/// <returns>True, if valid value between and including 0 and 1.</returns>
public static bool ValidateLevel(object value)
{
double? level = 0;
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
level = lvl;
}
}
if (value is double valdouble)
{
level = valdouble;
}
if (level.HasValue && level >= 0 && level <= 1)
return true;
else
return false;
}
/// <summary>
/// Constructor sets DataContext to itself.
/// </summary>
public RectangleLevel()
{
InitializeComponent();
this.DataContext = this;
}
/// <summary>
/// Updates the variables binded to the GradientStops for the rectangle.
/// </summary>
/// <param name="level">Level where the GradientStops should be. Valid value is 0 to 1 representing 0 to 100% filled.</param>
private void UpdateGradientStops(double level)
{
double scale = 0;
if (level < .5)
{
scale = level;
}
else if (level >= .5)
{
scale = 1 - level;
}
GradientFilledColor = level;
GradientEmptyColor = level + (level * .1 * scale);
}
}
}
MAINWINDOW XAML主窗口 XAML
<Window x:Class="RectDynamicGradient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RectDynamicGradient"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="TheSlider" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" LargeChange="0.1" Maximum="1" SmallChange="0.01" >
<Slider.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="2"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="2"/>
</TransformGroup>
</Slider.RenderTransform>
</Slider>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<TextBlock Text="SliderValue:"/>
<TextBlock HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<TextBlock Text="UC LEVEL Value:"/>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=MyUserControl, Path=RectLevel}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
<local:RectangleLevel x:Name="MyUserControl" Width="80" VerticalAlignment="Stretch" RectLevel="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
</Grid>
</Window>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.