我正在寻找使用模板和MVVM模式创建类似控件的Grid。这是我遇到的问题的简单描述。 我首先定义一个要为Grid的每个元素实例化的类Element:
public class Element : INotifyPropertyChanged
{
private string _dText = "Default";
public string dText { get { return _dText; } set { _dText = value; NotifyPropertyChanged("dText"); } }
internal Element(string aText) { dText = aText; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(info));
}
}
然后我在ViewModel类中实例化一些元素,这是一个Page的DataContext,如下所示:
public class MinVM
{
private Element _element0 = new Element("Element0 initialization text 0");
public Element element0 { get { return _element0; } set { _element0 = value; } }
private Element _element1 = new Element("Element1 initialization text 1");
public Element element1 { get { return _element1; } set { _element1 = value; } }
}
现在,在XAML Page.PageResources中,我创建了一个ControlTemplate,旨在为我提供一些很好的居中文本,如下所示:
<ControlTemplate x:Name="cTemplate" x:Key="CTemplate" TargetType="ContentControl">
<Grid x:Name="ctGrid" Background="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBox x:Name ="dtBox" Background="Orange" BorderThickness="0"
VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Text="{Binding dText, FallbackValue=Default control template text}">
</TextBox>
</Grid>
</ControlTemplate>
最后,在Page内容本身,我实例化其中的网格和元素,如下所示:
<ContentControl x:Name="element0" Grid.Row="0" Template="{StaticResource CTemplate}" Content="{Binding Path=element0}"></ContentControl>
<ContentControl x:Name="element1" Grid.Row="1" Template="{StaticResource CTemplate}" Content="{Binding Path=element1}"></ContentControl>
这一切都很好,正如你所料,但绑定不起作用,即我无法更改默认文本,因为WPF正在MinVM中寻找字段dText,而不是在Element内部。这是因为ControlTemplate忽略了Content =&#34; {Binding Path = element0}&#34;
尝试修复它的显而易见的方法是使用DataTemplate而不是ControlTemplate。我已经完成了 - 创建了一个与上述ControlTemplate完全相同的DataTemplate,Key =&#34; DTemplate&#34;。然后我将实例化XAML更改为
<ContentControl x:Name="element0" Grid.Row="0" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element0}"></ContentControl>
<ContentControl x:Name="element1" Grid.Row="1" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element1}"></ContentControl>
现在绑定正常工作,正如您所期望的那样 - 我可以愉快地更改控件文本。但渲染不再正常(文本不再在Grid元素中居中,而Orange不再填充Grid单元格)因为我已经丢失了ControlTemplate
所以,我可以让它正确渲染,也可以绑定,但不能同时渲染。
答案 0 :(得分:1)
如果我理解你的需要,那么下面的XAML适合我(你可以提取样式):
<Window x:Class="WpfScratch.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:WpfScratch"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ContentTemplate">
<TextBox TextAlignment="Center" Text="The obvious way to try and fix it is to use a DataTemplate instead of a ControlTemplate. I've done that - created a DataTemplate with exactly the same internals as the above ControlTemplate, with Key=DTemplate. I then change the instantiation XAML to" TextWrapping="Wrap"/>
</DataTemplate>
<Style x:Key="ContentControlStyle1" TargetType="{x:Type ContentControl}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ContentTemplate" Value="{StaticResource ContentTemplate}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ContentControl Margin="4" Style="{DynamicResource ContentControlStyle1}"/>
</Grid>
</Window>