简体   繁体   中英

How do I create a reusable TextBlock binding in WPF?

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.

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>
<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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM