嵌套的UserControls DataContext

时间:2013-03-28 17:56:14

标签: c# wpf

摘要:

我有两个UserControl名为ZoneZoneGroup。其中一个控件(ZoneGroup)包含另一个(Zone)的两个实例。他们都在DataContext事件处理程序中将根元素的this设置为Loaded

问题是DataContext内部控件(Zone s)在加载之前设置(DataContextChanged事件发生在Loaded之前),这会导致UI出现一些故障。 (在Zone内控制初始状态是错误的。)如果我阻止它,一切正常(至少似乎是!),除了我遇到以下错误报告。 (在“输出”窗口中)

public partial class Zone : UserControl
{
    ∙∙∙

    private void Zone_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        // Adding this if-clause solve UI problems but makes some binding errors!
        if (this.IsLoaded)
            brdRoot.DataContext = this;
    }
}

System.Windows.Data错误:40:BindingExpression路径错误:'object'''ZoneGroup'(Name ='')'上找不到'ZoneBrush'属性。 BindingExpression:路径= ZoneBrush; DataItem ='ZoneGroup'(Name =''); target元素是'brdRoot'(Name =''); target属性是'BorderBrush'(类型'Brush')

详细说明:

有一个名为UserControl的{​​{1}}包含多个数据绑定,如此...

Zone

因此,我将<UserControl x:Class="MyApp.Zone" ∙∙∙> <Border x:Name="brdRoot" BorderBrush="{Binding ZoneBrush}" BorderThickness="1"> ∙∙∙ </Border> </UserControl> 数据上下文设置为

brdRoot

此外,还有另外两个public partial class Zone : UserControl { public Brush ZoneBrush { get { return (Brush)GetValue(ZoneBrushProperty); } set { SetValue(ZoneBrushProperty, value); } } ∙∙∙ public Zone() { InitializeComponent(); } private void Zone_Loaded(object sender, RoutedEventArgs e) { brdRoot.DataContext = this; } ∙∙∙ } 有两个UserControl,以便包含和管理两个ContentPresenter控件。

Zone

代码隐藏是:

<UserControl x:Class="MyApp.ZoneGroup"
    ∙∙∙>
    <Border x:Name="brdRoot" BorderBrush="Gray" BorderThickness="1">
        <StackPanel Orientation="Horizontal">
            <ContentPresenter Content="{Binding MainZone}"
                              Margin="{Binding MainZonePadding}"/>
            <ContentPresenter Content="{Binding MemberZone}"/>
        </StackPanel>
    </Border>
</UserControl>

修改►草图: Two zone controls in a zone-group control.

我的应用程序正常运行,但报告了一些BindingExpression错误。

2 个答案:

答案 0 :(得分:1)

这不是一个直接的答案!

正如@HighCore所说,我尝试使用ItemsControl而不是在我的用户控件中实现两个ContentPresenter。为了清楚起见,我制作了一个新的简单应用程序,以便能够简单地描述它。所以请考虑一些新的假设:

这里再次有两个UserControl; MyItemMyItemsControl如下。

<UserControl x:Class="MyApp.MyItem"
             ∙∙∙>
    <Grid x:Name="grdRoot">
        <Border BorderBrush="{Binding ItemBorderBrsuh}" BorderThickness="1">
            <TextBlock x:Name="txtColorIndicator"
                       Text="Item"
                       TextAlignment="Center"
                       Margin="5"/>
        </Border>
    </Grid>
</UserControl>

C#代码隐藏:

public partial class MyItem : UserControl
{
    #region ________________________________________  ItemBorderBrsuh

    public Brush ItemBorderBrsuh
    {
        get { return (Brush)GetValue(ItemBorderBrsuhProperty); }
        set { SetValue(ItemBorderBrsuhProperty, value); }
    }

    public static readonly DependencyProperty ItemBorderBrsuhProperty =
        DependencyProperty.Register("ItemBorderBrsuh",
                                    typeof(Brush),
                                    typeof(MyItem),
                                    new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.None, OnItemBorderBrsuhPropertyChanged));

    private static void OnItemBorderBrsuhPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        MyItem instance = sender as MyItem;

        if (instance != null && e.NewValue is SolidColorBrush)
            instance.txtColorIndicator.Text = (e.NewValue as SolidColorBrush).Color.ToString();
    }

    #endregion

    public MyItem()
    {
        InitializeComponent();

        grdRoot.DataContext = this;
    }
}

这是MyItemsControl。

<UserControl x:Class="MyApp.MyItemsControl"
    ∙∙∙>
    <StackPanel>
        <TextBlock x:Name="txtHeader" Margin="0,0,0,5" TextAlignment="Center" Text="0 Item(s)"/>
        <Border BorderBrush="Gray" BorderThickness="1" Padding="5">
            <ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:MyItem />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Border>
    </StackPanel>
</UserControl>

C#代码隐藏:

public partial class MyItemsControl : UserControl
{
    private ObservableCollection<MyItem> _Items = new ObservableCollection<MyItem>();
    public ObservableCollection<MyItem> Items
    {
        get
        {
            return _Items;
        }
        set
        {
            _Items = value;
        }
    }

    private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        txtHeader.Text = Items.Count + " Item(s)";
    }

    public MyItemsControl()
    {
        InitializeComponent();
        Items.CollectionChanged += Items_CollectionChanged;

        this.DataContext = this;
    }
}

以下是MyItemMyItemsControl的使用方法。

<Grid>
    <local:MyItemsControl HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5" BorderBrush="Black" BorderThickness="1">
        <local:MyItemsControl.Items>
            <local:MyItem ItemBorderBrsuh="Green" Margin="1"/>
            <local:MyItem ItemBorderBrsuh="Red" Margin="1"/>
            <local:MyItem ItemBorderBrsuh="Blue" Margin="1"/>
            <local:MyItem ItemBorderBrsuh="Orange" Margin="1"/>
        </local:MyItemsControl.Items>
    </local:MyItemsControl>
</Grid>

现在,BindingExpression没有问题,但仍有一个重要问题。如何更换

{
    grdRoot.DataContext = this;
}

{
    this.DataContext = this;
}

使用真ViewModel

<强>截图:

MyItemsControl


编辑: 我尝试实现MVVM模式,但存在一些问题。我问了第一个here

答案 1 :(得分:1)

您所有UserControls以及所有DependencyProperties所有人都过度复杂化了。请看这个使用0行C#代码(仅限XAML)的示例:

<Window x:Class="MiscSamples.ItemsControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsControl" Height="300" Width="300">
    <Window.Resources>

ItemsControl的样式:

        <Style TargetType="ItemsControl" x:Key="ZoneItemsControlStyle">
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="Padding" Value="5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ItemsControl">
                        <Border BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Background="{TemplateBinding Background}"
                                Padding="5">
                            <DockPanel>
                                <TextBlock HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                           Text="{Binding Items.Count,RelativeSource={RelativeSource TemplatedParent}, StringFormat='{}{0} Item(s)'}"
                                           Foreground="{TemplateBinding Foreground}"
                                           DockPanel.Dock="Top"/>
                                <Border BorderBrush="{TemplateBinding BorderBrush}"
                                        BorderThickness="{TemplateBinding BorderThickness}"
                                        Padding="{TemplateBinding Padding}">
                                    <ItemsPresenter/>
                                </Border>
                            </DockPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>

DataTemplate For Brushes:

        <DataTemplate DataType="{x:Type Brush}">
            <Border BorderBrush="{Binding}" Margin="1" BorderThickness="1" Padding="2,3,2,3">
                <TextBlock Text="{Binding}" TextAlignment="Center" Foreground="Black"/>
            </Border>
        </DataTemplate>

    </Window.Resources>

现在,它的用法:

    <Grid>
        <ItemsControl VerticalAlignment="Center" HorizontalAlignment="Center"
                      Style="{StaticResource ZoneItemsControlStyle}">
            <SolidColorBrush Color="Red"/>
            <SolidColorBrush Color="Green"/>
            <SolidColorBrush Color="Black"/>
            <SolidColorBrush Color="Blue"/>
        </ItemsControl>
    </Grid>
</Window>

结果:

enter image description here

了解我如何利用DataTemplates来展示特定Data Type的自定义用户界面? (在这种情况下,System.Windows.Media.Brush类)

我&#34;使用画笔作为ViewModels&#34;。您当然也可以创建自己的ViewModel,然后为每种VM类型创建一个特定的DataTemplate

另外,请参阅我如何使用TemplateBinding MarkupExtension将ControlTemplate内的多个属性绑定到ItemsControl实例中的相应值。

最后,了解如何将ANY类商品实际添加到ItemsControl

另外,我必须提一下,我使用这种基于Style的方法来实现可重用性。您可以在应用程序中的其他位置放置另一个ItemsControl并设置其Style="{StaticResource ZoneItemsControlStyle}",然后您就完成了。但是如果你只计划一次使用它,你可以将所有属性硬编码放在ItemsControl.Template ControlTemplate中。