【问题标题】:How do I create a reusable TextBlock binding in WPF?如何在 WPF 中创建可重用的 TextBlock 绑定?
【发布时间】:2014-08-11 16:23:43
【问题描述】:

我一直在自学 WPF,并且仍在学习基本概念和术语。因此,如果此问题的标题措辞不正确,请原谅我。

我有以下 XAML,它是绑定到 ViewModelBase 类型对象的 HierarchicalDataTemplate 的一部分:

            <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 具有在 XAML 中引用的相应属性:

public TextBlockStyle PrefixStyle...
public TextBlockStyle ValueStyle...
public TextBlockStyle SuffixStyle... 


public class TextBlockStyle : INotifyPropertyChanged
{
    public string Text...
    public FontWieght Weight...
    public Brush Foreground 
}

我怎样才能在 XAML 中只定义一次 TextBlockTextBlockStyle 的绑定,而不必像上面那样显式绑定每个 TextBlock 属性?所以我可以为每个TextBlock 写一行:

                <StackPanel Orientation="Horizontal">
                    <TextBlock Source="{Binding PrefixStyle}" />                           
                    <TextBlock Source="{Binding ValueStyle}"  />                    
                    <TextBlock Source="{Binding SuffixStyle}" />
                 ...                                                              
                </StackPanel>

我只是不知道从哪里开始。我需要继承 TextBlock 吗?使用绑定组?

这一定是程序员遇到的常见问题 - 如果之前有人问过这个问题,我深表歉意。我试过搜索,但我对 WPF 太陌生了,不知道如何正确表达我的问题。

【问题讨论】:

  • 请注意,如果您尝试使用的代码不起作用,您可能会在某一时刻拼写错误 Prefix,然后写成 Pefix
  • @AlastairCampbell 谢谢。我会编辑帖子。这不是我真正的代码。只是用它来澄清我的问题。

标签: c# wpf xaml


【解决方案1】:

您使用隐式样式在您的应用程序中设置“全局”样式。这通常在ResourceDictionary 内的App.xaml 文件中完成。

<Style TargetType="{x:Type TextBlock}"
       BasedOn="{StaticResource TextBlockStyle}">
    <Setter Property="FontWeight" 
            Value="Bold">
    </Setter>
    <Setter Property="Foreground" 
            Value="Red">
    </Setter>
</Style>

如果您继续使用您所遵循的方法(我不会),您可以将您的设置器更改为:

<Setter Property="FontWeight" 
        Value="{Binding Weight}">
</Setter>

然后,您的应用程序中的所有TextBlocks 都将使用该样式,除非您为控件定义更本地化的显式/隐式样式。

编辑以详细说明 cmets:

我也在使用TreeViewHierarchicalDataTemplate。以 MVVM 方式思考,TreeView(父/子)中的每个项目都应该代表某种模型。例如,想想 Windows 资源管理器。在 MVVM 世界中,其中的每个项目都是 FolderDriveFolderDrive 不会有不同的字体粗细/颜色/大小特征,因为这都是与视图相关的。

在我们的例子中,你会有这样的东西:

public class BaseItem : ViewModel
{
    public ObservableCollection<BaseItem> Children { .... } 
    public bool IsSelected { .... }
    public string Title { .... } 
}

因为一个文件夹可以容纳更多的文件夹,而一个驱动器可以容纳文件夹,你会喜欢这样的:

public class DriveVM : BaseItem { }
public class FolderVM : BaseItem { }

你会在DriveVM 内部做什么Children.Add(new FolderVM(folder));,这将显示驱动器下的一个文件夹。问题是这可能会变得非常复杂。简而言之,我认为继承是使用TreeView 的关键。

另一个选项是这样的:

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

然后在你的HierarchicalDataTemplate 中做:

<StackPanel Orientation="Horizontal">
    <TextBlock DataContext="{Binding Prefix}"
               Style="{StaticResource PrefixTextBlockStyle} ">  
    ....                              
</StackPanel>

【讨论】:

  • 你能详细说明你为什么不使用这种方法吗?我是在做错事还是让自己更难思考?
  • 主要是因为所有这些内容都与 View 相关。在使用 WPF 时应该完成的 MVVM 之后,将 ViewModel 和 View 耦合在一起。如果您正在编写某种文本编辑器,那么我也可能是错的,那么我认为您还可以。我只是想提出来让大家知道。
  • 谢谢。我不是在做文本编辑器。该模板用于 TreeViewItem。我正在从具有数十个 TreeNode 子类的 Winforms 控件库中移植东西,每个子类都有不同的表示形式。并且我试图找到保持每个节点的独特表示,而不必创建十几个不同的模板。所以现在我继承 ViewModels 而不是 TreeNodes。我感觉我没有正确使用 MVVM....
【解决方案2】:
<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>

或者在 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>

在别处使用:

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

【讨论】:

  • 谢谢。我想这可能是我正在寻找的。但是如果我想在多个 StackPanel 或其他用户控件中使用它怎么办?
  • 您可以创建一个全局命名样式而不是隐式样式,并且只需在每个文本框上使用 Style="MyTextBoxStyle",或者使用类似于上面的样式为特定元素的所有文本框设置它。
猜你喜欢
  • 2013-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 2011-07-03
相关资源
最近更新 更多