Sorry for the terrible Question Title, I don't know how to phrase this, feel free to comment.
I am working on a WPF desktop app using the MVVM pattern. I have a great number of text fields on my ViewModel, all of which I want to display using the pattern below (greatly simplified):
<StackPanel Orientation="Vertical">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomePredicate}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</St开发者_如何学GoackPanel.Style>
<Label Content="SomeHeader:"/>
<TextBlock Text="{Binding Path=SomeText}" />
</StackPanel>
The display always consists of:
- A Predicate for determining if the StackPanel should be shown
- A string used as header text. This can be set in XAML, it doesn't have to come from the viewmodel
- A binding to the Textblock text string on the ViewModel
I would really love to be able to define these like this:
<MyHeaderedTextBlockControl Text="{Binding Path=SomeText}"
Header="SomeHeader:"
Predicate="{Binding SomePredicate}"/>
Can this be done? I tried doing it with a UserControl but had no idea what I was doing.
It is important to me that the binding modes remain working, ie if the Text
binding is in OneWay
or TwoWay
mode, the TextBlock
should update when the Text
property on the ViewModel raises OnPropertyChanged
.
I also dont like the idea of doing this with a View and a ViewModel for each such text property, because then I have to create those ViewModels and wire them up for updates etc. I want a solution that I can implement in the View, ideally the ViewModel shouldn't even know about it.
With UserControls you define dependency properties in your code and forward them to the "template" via binding, e.g.:
<UserControl x:Class="Test.UserControls.HeaderedTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="control">
<StackPanel Orientation="Vertical">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding Predicate, ElementName=control}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label Content="{Binding Header, ElementName=control}" />
<TextBlock Text="{Binding Text, ElementName=control}" />
</StackPanel>
</UserControl>
namespace Test.UserControls
{
public partial class HeaderedTextBlock : UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty PredicateProperty =
DependencyProperty.Register("Predicate", typeof(bool), typeof(HeaderedTextBlock), new UIPropertyMetadata(false));
public bool Predicate
{
get { return (bool)GetValue(PredicateProperty); }
set { SetValue(PredicateProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public HeaderedTextBlock()
{
InitializeComponent();
}
}
}
Would something like this work for you?
精彩评论