用户控件依赖属性更新多个控件值



我有一种感觉,我的基本问题是绑定一些内部属性。

在usercontrol中,我有一个包含线性梯度的矩形。我创建了一个依赖属性,以便能够给出一个值(0到1)来指定渐变线在矩形中的位置。为了动态地调整这个值,我连接了一个滑块来进行测试。

我还添加了一些反馈文本块,让我知道一些数据是如何流动的,如果它正在流动。这样我就可以把绑定设置为GradientStops。我的用户控件属性中的偏移值不起作用。如何让用户控件通过改变RectangleLevel来更新矩形渐变。RectLevel价值?

用户控件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>
<<p>用户控件代码/strong>
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);
}
}
}

主窗口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>
<<p>主窗口代码/strong>
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

经过评论的反馈,我们进行了改进。

  • DataContext删除了这个,并添加了相对源到用户控件(名为"uc")

  • 将NotifyProperties更改为DependencyProperties

取代DataContext

<GradientStop Color="Cyan" Offset="{Binding ElementName=uc, Path=GradientFilledColor}"/>

更新的依赖项属性

/// <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));

DependencyProperty提供与INotifyProperty PropertChanged事件相同的功能(尽管使用不同的机制)。正如@Clemens所提到的,将datacontext设置为self是一个糟糕的想法,当用户控件在整个项目中使用并且datacontext设置为其他东西时,会断开链接。

我仍然愿意接受有助于支持良好实践和学习的建议。

<<p>

更新代码/strong>用户控件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>

<<p>用户控件代码/strong>
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);
}
}
}

主窗口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>

最新更新