簡體   English   中英

誤解的數據綁定基礎知識和DataContexts-長話不說

[英]Misunderstanding databinding fundamentals and DataContexts — long story

我一直在幾種簡單的情況下使用數據綁定,並取得了很好的成功。 通常,我只是使用INotifyPropertyChanged來啟用我的代碼隱藏功能來修改屏幕上的GUI值,而不是為所有內容實現依賴項屬性。

我正在玩LED控件,以了解有關用戶控件中數據綁定的更多信息,並且由於VS2008告訴我必須這樣做,因此不得不使用依賴項屬性。 我的應用程序很簡單-我有一個窗口,其中顯示幾個LED控件,每個控件上方都有一個數字,還可以在側面顯示一個。 LED應使用默認顏色以及更改狀態進行定義。

我首先編寫了一個LED控件,看起來還不錯。 首先,我從這樣的代碼開始:

LED.xaml

<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>

這樣就可以畫一個LED了。 然后,通過設置this.DataContext = this像我一直一樣,將LEDSize,LEDLabel和LEDColor綁定到Ellipse屬性。

LED.xaml.cs

/// <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控件可以隨着時間的推移在其他小部件中重復使用,而我的下一步是創建另一個名為IOView 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屬性,一切都會按預期進行:

替代文字

然后,我盲目地嘗試將LEDColor數據綁定到我的IOView,VS2008友好地告訴我“不能在類型為“ LED”的“ LEDColor”屬性上設置“綁定”。只能在DependencyObject的DependencyProperty上設置“ Binding” “。 哎呀! 我什至沒有意識到,因為我以前沒有做過自己的GUI控件。 由於LEDColor已經與Ellipse進行數據綁定,因此我添加了一個名為Color的DependencyProperty。

LED.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;
        }
    }

請注意,我在設置器中設置了屬性LEDColor ,因為這就是Ellipse知道其應為哪種顏色的方式。

下一步是通過綁定到IOView.InputColor設置IOView中LED的顏色:

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路徑錯誤:在“對象”“ LED”(Name =“)”上找不到“ InputColor”屬性。 BindingExpression:路徑= InputColor; DataItem ='LED'(Name =''); 目標元素是'LED'(Name =''); 目標屬性為“顏色”(類型為“畫筆”)

嗯...由於某種原因,我的DataBinding搞砸了。 我可以使LED與數據綁定一起獨立工作,但是一旦將它包裝在另一個控件中並設置了它的datacontext后,它就不起作用了。 我不確定目前該如何嘗試。

我希望得到盡可能詳細的答案。 我知道我可以重新設計一個CheckBox以獲得相同的結果,但這對我來說是一個實驗,我試圖了解如何將數據綁定到控件的后代。

關於這一切,有很多要說的,但讓我看看我是否可以提供解決您一些誤解的指針:

  • 為了使屬性成為綁定的目標 ,該屬性必須是依賴項屬性。 WPF(和Silverlight)使用依賴項屬性來跟蹤更改,支持值優先級(用於動畫等)以及其他許多有用的東西。 請注意,我說的是“目標”。 綁定的來源可以是任何支持更改通知的舊對象。
  • UserControl本身中設置UserControlDataContext被認為是不好的做法,因為UserControl任何使用者都可以更改它,並且這樣做會破壞控件中依賴於該上下文的任何綁定。
  • 除了上述要點之外,另一個問題是,您將破壞依賴於用戶控件“上方”的數據上下文的使用代碼中的所有綁定。 這就解釋了您遇到的與InputColor未成功綁定的問題。 InputColor屬性位於主機控件( IOView )提供的數據上下文中,但是LED的數據上下文設置為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();
    }
}

LED.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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM