简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM