代码之家  ›  专栏  ›  技术社区  ›  Dave

误解数据绑定基础和数据上下文-长话短说

  •  2
  • Dave  · 技术社区  · 14 年前

    我正在玩一个LED控件来学习更多关于用户控件中数据绑定的知识,并且被迫使用依赖属性,因为VS2008告诉我必须这样做。我的应用程序很简单——我有一个窗口,其中显示几个LED控件,每个控件上面都有一个数字,也可以选择在其旁边显示一个。LED应该可以用默认颜色定义,也可以改变状态。

    我开始写一个LED控制,这似乎非常好。首先,我从这样的代码开始:

    <UserControl x:Class="LEDControl.LED"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="Auto" Width="Auto">
    
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <!-- LED portion -->
            <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}" Fill="{Binding LEDColor}" StrokeThickness="2" Stroke="DarkGray" />
            <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}">
                <Ellipse.Fill>
                    <RadialGradientBrush GradientOrigin="0.5,1.0">
                        <RadialGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
                                <TranslateTransform X="0.02" Y="0.3"/>
                            </TransformGroup>
                        </RadialGradientBrush.RelativeTransform>
                        <GradientStop Offset="1" Color="#00000000"/>
                        <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <!-- label -->
            <TextBlock Grid.Column="1" Margin="3" VerticalAlignment="Center" Text="{Binding LEDLabel}" />
        </Grid>
    </UserControl>
    

    this.DataContext = this 就像我一直做的那样:

    /// <summary>
    /// Interaction logic for LED.xaml
    /// </summary>
    public partial class LED : UserControl, INotifyPropertyChanged
    {
        private Brush state_color_;
        public Brush LEDColor
        {
            get { return state_color_; }
            set { 
                state_color_ = value;
                OnPropertyChanged( "LEDColor");
            }
        }
    
        private int led_size_;
        public int LEDSize
        {
            get { return led_size_; }
            set {
                led_size_ = value;
                OnPropertyChanged( "LEDSize");
            }
        }
    
        private string led_label_;
        public string LEDLabel
        {
            get { return led_label_; }
            set {
                led_label_ = value;
                OnPropertyChanged( "LEDLabel");
            }
        }
    
        public LED()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    
        #region INotifyPropertyChanged Members
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged( string property_name)
        {
            if( PropertyChanged != null)
                PropertyChanged( this, new PropertyChangedEventArgs( property_name));
        }
    
        #endregion
    }
    

    在这一点上,我可以更改属性值,并看到LED改变大小、颜色和标签。太好了!

    我希望LED控件可以在我编写的其他小部件中重用,下一步是创建另一个UserControl(在单独的程序集中),名为 IOView . IOView公司 在这一点上是非常基本的:

    IOView.xaml文件

    <UserControl x:Class="IOWidget.IOView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:led="clr-namespace:LEDControl;assembly=LEDControl"
        Height="Auto" Width="Auto">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" HorizontalAlignment="Center" Text="{Binding Path=Index}" />
            <led:LED Grid.Row="1" HorizontalContentAlignment="Center" HorizontalAlignment="Center" LEDSize="30" LEDColor="Green" LEDLabel="Test" />
        </Grid>
    </UserControl>
    

    请注意,我可以在设计时修改XAML中的LED属性,一切都按预期工作:

    alt text

    “不能对类型为'LED'的'LEDColor'属性设置'Binding'。”。只能在DependencyObject的DependencyProperty上设置'Binding' 哎呀!我甚至没有意识到这一点,因为我还没有自己的图形用户界面控件之前。自从 LEDColor 已数据绑定到椭圆,我添加了一个名为Color的DependencyProperty。

    发光二极管.xaml.cs

        public static DependencyProperty ColorProperty = DependencyProperty.Register( "Color", typeof(Brush), typeof(LED));
        public Brush Color
        {
            get { return (Brush)GetValue(ColorProperty); }
            set { 
                SetValue( ColorProperty, value);
                LEDColor = value;
            }
        }
    

    注意,我设置了属性 发光二极管 在setter中,因为椭圆就是这样知道它应该是什么颜色的。

    下一步是通过绑定到设置IOView中LED的颜色IOView.InputColor:

    IOView.xaml.cs:

    /// <summary>
    /// Interaction logic for IOView.xaml
    /// </summary>
    public partial class IOView : UserControl, INotifyPropertyChanged
    {
        private Int32 index_;
        public Int32 Index
        {
            get { return index_; }
            set {
                index_ = value;
                OnPropertyChanged( "Index");
            }
        }
    
        private Brush color_;
        public Brush InputColor
        {
            get { return color_; }
            set {
                color_ = value;
                OnPropertyChanged( "InputColor");
            }
        }
    
        private Boolean state_;
        public Boolean State
        {
            get { return state_; }
            set {
                state_ = value;
                OnPropertyChanged( "State");
            }
        }
    
        public IOView()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    
        #region INotifyPropertyChanged Members
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged( string property_name)
        {
            if( PropertyChanged != null)
                PropertyChanged( this, new PropertyChangedEventArgs( property_name));
        }
    
        #endregion
    }
    

    而且在IOView.xaml文件,我将LED改为:

    <led:LED Grid.Row="1" HorizontalContentAlignment="Center" HorizontalAlignment="Center" LEDSize="30" Color="{Binding InputColor}" />
    

    BindingExpression路径错误:在“object'''LED'(Name='')上找不到“InputColor”属性。装订xpression:Path=InputColor;DataItem='LED'(Name='');目标元素为'LED'(Name='');目标属性为'Color'(键入'Brush')

    隐马尔可夫模型。。。所以出于某种原因,我的数据绑定搞砸了。我可以通过数据绑定让LED自己工作,但是一旦我将它包装到另一个控件中并设置它的datacontext,它就不工作了。我不知道在这一点上该尝试什么。

    1 回复  |  直到 14 年前
        1
  •  5
  •   Tim Cooper    13 年前

    • 为了让房产成为 对于绑定,该属性必须是依赖属性。WPF(和Silverlight)使用依赖属性作为跟踪更改的手段,支持值优先级(对于动画等),以及其他一些有用的东西。注意,我说的是“目标”。这个 来源 绑定的类型可以是任何支持更改通知的旧对象。
    • 设置 UserControl DataContext 用户控件
    • 除上述问题外,另一个问题是您将在中中断任何绑定 依赖于用户控件“上面”的数据上下文的代码。这就解释了你所看到的问题 InputColor 未成功绑定。这个 输入颜色 属性位于宿主控件提供的数据上下文中( IOView )但是LED的数据上下文被设置为LED本身,因此如果不进一步限定绑定,就无法找到该属性。

    遵循此建议将导致以下实现(未测试):

    发光二极管.xaml.cs

    public partial class LED : UserControl
    {
        public static readonly DependencyProperty LEDColorProperty = DependencyProperty.Register(
            "LEDColor",
            typeof(Brush),
            typeof(LED));
    
        public Brush LEDColor
        {
            get { return this.GetValue(LEDColorProperty) as Brush; }
            set { this.SetValue(LEDColorProperty, value); }
        }
    
        // LEDSize and LEDLabel omitted for brevity, but they're very similar to LEDColor
    
        public LED()
        {
            InitializeComponent();
        }
    }
    

    发光二极管.xaml :

    <UserControl
        x:Name="root"
        x:Class="LEDControl.LED"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="Auto" Width="Auto">
    
        <Grid DataContext="{Binding ElementName=root}>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <!-- LED portion -->
            <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}" Fill="{Binding LEDColor}" StrokeThickness="2" Stroke="DarkGray" />
            <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}">
                <Ellipse.Fill>
                    <RadialGradientBrush GradientOrigin="0.5,1.0">
                        <RadialGradientBrush.RelativeTransform>
                            <TransformGroup>
                                <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
                                <TranslateTransform X="0.02" Y="0.3"/>
                            </TransformGroup>
                        </RadialGradientBrush.RelativeTransform>
                        <GradientStop Offset="1" Color="#00000000"/>
                        <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <!-- label -->
            <TextBlock Grid.Column="1" Margin="3" VerticalAlignment="Center" Text="{Binding LEDLabel}" />
        </Grid>
    </UserControl>
    

    IOView.xaml文件 :

    <UserControl x:Name="root"
        x:Class="IOWidget.IOView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:led="clr-namespace:LEDControl;assembly=LEDControl"
        Height="Auto" Width="Auto">
    
        <Grid DataContext="{Binding ElementName=root}">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" HorizontalAlignment="Center" Text="{Binding Path=Index}" />
            <led:LED Grid.Row="1" HorizontalContentAlignment="Center" HorizontalAlignment="Center" LEDSize="{Binding I_Can_Bind_Here_All_I_Like}" LEDColor="{Binding I_Can_Bind_Here_All_I_Like}" LEDLabel="{Binding I_Can_Bind_Here_All_I_Like}" />
        </Grid>
    </UserControl>