摘要:
我有两个UserControl
名为Zone
和ZoneGroup
。其中一个控件(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>
修改►草图:
我的应用程序正常运行,但报告了一些BindingExpression错误。
答案 0 :(得分:1)
这不是一个直接的答案!
正如@HighCore所说,我尝试使用ItemsControl
而不是在我的用户控件中实现两个ContentPresenter
。为了清楚起见,我制作了一个新的简单应用程序,以便能够简单地描述它。所以请考虑一些新的假设:
这里再次有两个UserControl
; MyItem
和MyItemsControl
如下。
<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;
}
}
以下是MyItem
中MyItemsControl
的使用方法。
<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
?
<强>截图:强>
编辑: 我尝试实现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>
结果:
了解我如何利用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中。