Reputation: 1364
I've been teaching myself WPF and I'm still learning the basic concepts and terminology. So please forgive me if the title of this question isn't worded correctly.
I have the following XAML that's part of a HierarchicalDataTemplate
bound to an object of type ViewModelBase
:
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PrefixStyle.Text}"
FontWeight="{Binding PrefixStyle.Weight}"
Foreground ="{Binding PrefixStyle.Foreground}"
Margin="0,0,3,0"/>
<TextBlock Text="{Binding ValueStyle.Text}"
FontWeight="{Binding ValueStyle.Weight}"
Foreground ="{Binding ValueStyle.Foreground}"
Margin="0,0,3,0"/>
<TextBlock Text="{Binding SuffixStyle.Text}"
FontWeight="{Binding SuffixStyle.Weight}"
Foreground ="{Binding SuffixStyle.Foreground}"
Margin="0,0,3,0"/>
...
</StackPanel>
ViewModelBase
has the corresponding properties referenced in the XAML:
public TextBlockStyle PrefixStyle...
public TextBlockStyle ValueStyle...
public TextBlockStyle SuffixStyle...
public class TextBlockStyle : INotifyPropertyChanged
{
public string Text...
public FontWieght Weight...
public Brush Foreground
}
How can I define the binding for TextBlock
to TextBlockStyle
only once in the XAML and save myself from having to explicitly bind each TextBlock
property as above? So I can just have one line for each TextBlock
:
<StackPanel Orientation="Horizontal">
<TextBlock Source="{Binding PrefixStyle}" />
<TextBlock Source="{Binding ValueStyle}" />
<TextBlock Source="{Binding SuffixStyle}" />
...
</StackPanel>
I just don't know where to start. Do I need to subclass TextBlock? Use a BindingGroup?
This must be common issue programmers run into - so I apologize if this question has been asked before. I've tried searching, but I'm so new to WPF I don't know how to properly express my question.
Upvotes: 2
Views: 1025
Reputation: 5163
You use implicit styling to set a "global" style across your application. This is usually done in your App.xaml
file inside of the ResourceDictionary
.
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="FontWeight"
Value="Bold">
</Setter>
<Setter Property="Foreground"
Value="Red">
</Setter>
</Style>
If you continue to use the method you're following (I wouldn't), you can change your setters to something like:
<Setter Property="FontWeight"
Value="{Binding Weight}">
</Setter>
Then, all of the TextBlock
s in your application would use that style unless you define an explicit/implicit style more local to the control.
Edit to elaborate the comments:
I am also using a TreeView
and HierarchicalDataTemplate
. Thinking in MVVM fashion, each item in the TreeView
(parent/child) should represent some sort of Model. For instance, think of Windows Explorer. Each item in it would be a Folder
or Drive
in MVVM world. Folder
and Drive
wouldn't have different font weight/color/size characteristics because that's all view related.
In our example, you would have something like this:
public class BaseItem : ViewModel
{
public ObservableCollection<BaseItem> Children { .... }
public bool IsSelected { .... }
public string Title { .... }
}
Since a folder can hold more folders, and a drive can holder folders you would something like:
public class DriveVM : BaseItem { }
public class FolderVM : BaseItem { }
Which would all you to do Children.Add(new FolderVM(folder));
inside of DriveVM
, and that would show a folder under a drive. The problem is this can get pretty complex. In short though, I think inheritance is the key to using a TreeView
.
Another option is something like this:
<Style x:Name="PrefixTextBlockStyle"
TargetType="{x:Type TextBox}">
<Setter Property="FontWeight"
Value="Bold" />
<Setter Property="Foreground"
Value="Red" />
<Setter Property="Text"
Value="{Binding Text}"
<Setter Property="Margin"
Value="0 0 3 0" />
</Style>
<Style x:Name="SuffixTextBlockStyle"
TargetType="{x:Type TextBox}">
<Setter Property="FontWeight"
Value="Italic" />
<Setter Property="Foreground"
Value="Orange" />
<Setter Property="Text"
Value="{Binding Text}" />
<Setter Property="Margin"
Value="0 0 3 0" />
</Style>
and then in your HierarchicalDataTemplate
do:
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding Prefix}"
Style="{StaticResource PrefixTextBlockStyle} ">
....
</StackPanel>
Upvotes: 3
Reputation: 21261
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Text}" />
<Setter Property="FontWeight" Value="{Binding Weight}" />
<Setter Property="Foreground " Value="{Binding Foreground}" />
<Setter Property="Margin" Value="0,0,3,0" />
</Style>
</StackPanel.Resources>
<TextBlock DataContext="{Binding PrefixStyle}"/>
<TextBlock DataContext="{Binding ValueStyle}"/>
<TextBlock DataContext="{Binding SuffixStyle}"/>
...
</StackPanel>
Or using a global named style in app.xaml:
<Application>
<Application.Resources>
<Style x:Key="MyTextBlockStyle" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Text}" />
<Setter Property="FontWeight" Value="{Binding Weight}" />
<Setter Property="Foreground " Value="{Binding Foreground}" />
<Setter Property="Margin" Value="0,0,3,0" />
</Style>
</Application.Resources>
</Application>
Usage elsewhere:
<Window>
<StackPanel>
<!-- Single textblock with explicit style -->
<TextBlock DataContext="Blah" Style="{StaticResource MyTextBlockStyle}" />
<!-- Multiple textblocks with implicit style -->
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock" BasedOn={StaticResource MyTextBlockStyle}" />
</StackPanel.Resources>
<TextBlock DataContext="{Binding PrefixStyle}"/>
<TextBlock DataContext="{Binding ValueStyle}"/>
<TextBlock DataContext="{Binding SuffixStyle}"/>
...
</StackPanel>
</Window>
Upvotes: 1