【问题标题】:WPF - Display TextBlock with different colorsWPF - 显示不同颜色的文本块
【发布时间】:2018-12-06 11:40:29
【问题描述】:

所以我想做的是显示一个TextBlock,每行使用不同的颜色,理想情况下我想使用绑定。

我的 TextBlock 可能会显示一个 Items 列表,每个 Item 都有 Texte 和 Color 属性。使用 foreach,我想以指定的颜色显示每个项目的一行,并使用 Texte 属性。

我已经尝试了以下方法:

1)我创建了一个 TextBlock,它的 Text 绑定到 ViewModel 中的字符串属性,并使用 foreach 我只是填充字符串,但在这种情况下,我无法在每一行上应用我想要的颜色。

2)我发现堆栈上的link 有点帮助,建议使用Run

所以在我的 ViewModel 中,我像这样填充 TextBlock:

private TextBlock legende;
public TextBlock Legende
{
   get { return legende; }
   set
   {
     legende = value;
     this.NotifyPropertyChanged("Legende");
   }
}
public void UpdateLegend(Legend legende)
{
    this.Legende = new TextBlock();
    this.Legende.TextWrapping = TextWrapping.Wrap;
    this.Legende.Margin = new Thickness(10);
    this.Legende.FontSize = 14;
    this.Legende.LineStackingStrategy=LineStackingStrategy.BlockLineHeight;
    this.Legende.LineHeight = 20;
    int i = 0;
    foreach(LegendItem item in legende.list)
    {
      if(i==0)
      {
        Run run = new Run(item.Texte);
        run.Foreground = item.Color;
        this.Legende.Inlines.Add(run);
      }
      else
      {
         Run run = new Run(")\r\n"+item.Texte);
         run.Foreground = item.Color;
         this.Legende.Inlines.Add(run);
      }

      i++;
    }
}

每次我需要更新图例时,我的模型都会执行以下操作:

contexte.UpdateLegend(MonGraphe.Legende);
this.CanvasLegend = contexte.Legende;

然后是我的观点:

<Grid Grid.Column="2" Background="WhiteSmoke">
    <TextBlock x:Name="CanvasLegend" TextAlignment="Left" HorizontalAlignment="Left"/>
</Grid>

现在它根本不起作用,我找不到原因。 我也想在我的 ViewModel 中做所有事情,但为此我需要将我的 Textlock 绑定到我的 ViewModel 中定义的 TextBlock,但找不到怎么做?

编辑:

正如 Icebat 所解释的,我使用转换器如下:

XML 与 IceBat 相同

视图模型:

public class ColoringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //is your collection IEnumerable<>? If not, adjust as needed
            var legende = value as Legend;
            if (legende == null) return value;

            return legende.list.Select(i => new Run() { Text = i.Texte, Foreground = i.Color }).ToArray();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    class ViewModelMainWindow : INotifyPropertyChanged
    {
        private Legend legende;
        public Legend Legende
        {
            get { return legende; }
            set
            {
                legende = value;
                this.NotifyPropertyChanged("Legende");
            }
        }
}

还包括我的“Legende”课程,因为我认为这是存在误解的地方:

public class LegendItem 
    {
        public int Type { get; set; }
        public double Diametre { get; set; }
        public double Longueur { get; set; }
        public double Profondeur { get; set; }
        public string Texte { get; set; }
        public Brush Color { get; set; }
        public LegendItem()
        {

        }
    }
    public class Legend : INotifyPropertyChanged
    {
        private ObservableCollection<LegendItem> liste;
        public ObservableCollection<LegendItem> Liste
        {
            get { return liste; }
            set
            {
                liste=value;
                NotifyPropertyChanged(ref liste, value);
            }
        }
        public Legend()
        {
            this.list = new ObservableCollection<LegendItem>();
        }
        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        private bool NotifyPropertyChanged<T>(ref T variable, T valeur, [CallerMemberName] string nomPropriete = null)
        {
            if (object.Equals(variable, valeur)) return false;

            variable = valeur;
            NotifyPropertyChanged(nomPropriete);
            return true;
        }
        public Legend(Repere rep)
        {
            List < Brush >  listColors = new List<Brush>();
            listColors.Add(Brushes.Red);
            listColors.Add(Brushes.MediumBlue);
            listColors.Add(Brushes.Purple);
            listColors.Add(Brushes.LimeGreen);
            listColors.Add(Brushes.DarkOrange);
            listColors.Add(Brushes.Navy);
            listColors.Add(Brushes.DarkRed);
            listColors.Add(Brushes.Chartreuse);
            listColors.Add(Brushes.DodgerBlue);
            listColors.Add(Brushes.Tomato);

            this.list = new ObservableCollection<LegendItem>();
            List<Percage> listPer = rep.liste_percages;
            List<GroupePercage> listeGp = rep.listeGpPercages;


            foreach (Percage per in listPer)
            {
                LegendItem itemExists = this.list.FirstOrDefault(x => x.Type == per.Type && x.Diametre == per.Diametre && x.Profondeur == per.Depth&&x.Longueur==per.Longueur);
                if (itemExists == null)
                {
                    LegendItem newItem = new LegendItem();

                    newItem.Type = per.Type;
                    switch (newItem.Type)
                    {
                        case 51: newItem.Texte = "DRILL "; break;
                        case 58: newItem.Texte = "CounterSink "; break;
                        case 59: newItem.Texte = "Tapping "; break;
                        case 12: newItem.Texte = "Slot "; break;
                        default: newItem.Texte = "NOT FOUND "; break;
                    }
                    newItem.Diametre = per.Diametre;
                    newItem.Longueur = per.Longueur;
                    newItem.Texte += newItem.Diametre.ToString();
                    if (newItem.Type==12)
                    {
                        newItem.Texte = newItem.Diametre + " x " + newItem.Longueur;
                    }
                    newItem.Profondeur = per.Depth;
                    this.list.Add(newItem);
                }
            }
            foreach (GroupePercage per in listeGp)
            {
                LegendItem itemExists = this.list.FirstOrDefault(x => x.Type == per.Type && x.Diametre == per.Diametre && x.Profondeur == per.Depth && x.Longueur == per.Longueur);
                if (itemExists == null)
                {
                    LegendItem newItem = new LegendItem();
                    newItem.Type = per.Type;
                    switch (newItem.Type)
                    {
                        case 51: newItem.Texte = "DRILL "; break;
                        case 58: newItem.Texte = "CounterSink "; break;
                        case 59: newItem.Texte = "Tapping "; break;
                        case 12: newItem.Texte = "Slot "; break;
                        default: newItem.Texte = "NOT FOUND "; break;
                    }
                    newItem.Diametre = per.Diametre;
                    newItem.Longueur = per.Longueur;
                    newItem.Texte += newItem.Diametre.ToString();
                    if (newItem.Type == 12)
                    {
                        newItem.Texte = newItem.Diametre + "x" + newItem.Longueur;
                    }
                    newItem.Profondeur = per.Depth;
                    this.list.Add(newItem);
                }
            }
            for(int i=0;i<this.list.Count();i++)
            {
                this.list[i].Color = listColors[Math.Min(i,9)];
            }
        }
    }

【问题讨论】:

    标签: c# wpf binding textblock


    【解决方案1】:

    嗯,使用 Runs 的方法似乎是最简单的一种。不知道你哪里出错了,但这一切都应该相对容易。您可以使用 ItemsControl 进行内联:

    <TextBlock x:Name="CanvasLegend" TextAlignment="Left" HorizontalAlignment="Left">
        <TextBlock.Inlines>
            <ItemsControl ItemsSource="{Binding LegendItems, ElementName=me, 
                                        Converter={StaticResource coloringConverter}}" />
        </TextBlock.Inlines>
    </TextBlock>
    

    为了简单起见,我在这里使用ElementName 指向主机窗口,但您可以将其绑定到您的 ViewModel。 转换器如下所示:

    public class ColoringConverter : IValueConverter
    {
      public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
      {
         //is your collection IEnumerable<>? If not, adjust as needed
         var legend = value as IEnumerable<LegendItem>;
         if (legend == null) return value;
    
         return legend.Select(i => new Run() { Text = i.Text, Foreground = i.Color }).ToArray();
      }
    
      public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
      {
         throw new NotImplementedException();
      }
    }
    

    然后只需将转换器添加到资源中的某处(在我的示例中是窗口):

    <Window.Resources>
        <local:ColoringConverter x:Key="coloringConverter" />
    </Window.Resources>
    

    【讨论】:

    • 只是可以解决我的问题但没有绑定,所以会尝试你的方法,也许我今天可以再次学习一些新东西:)。所以我都明白(感谢 ListView 不知道我能做到这一点),除了一件事:我可以在哪里写我的转换器?我已经尝试过多次使用这些转换器,但始终无法做到 ^^'
    • @Siegfried.V,我将ListView 更改为ItemsControl 以避免突出显示项目。您可以在没有绑定的情况下执行此操作,但学习使用它们对 WPF 编程非常有益。您可以在 TextBlock 所在的位置编写转换器。您甚至可以在 Grid 中使用 &lt;Grid.Resources&gt;&lt;/Grid.Resources&gt; 而不是像我的示例中那样使用 Window。
    • 是的,谢谢,事实上这很长我试图理解这些“转换器”,我认为是时候了......你告诉我函数 public class ColoringConverter : IValueConverter 是 XAML 代码?它看起来不像 XAML?
    • @Siegfried.V,哦,你说的是代码本身。您可以将其放置在您正在使用的窗口或控件的代码隐藏中。在较大的项目中,转换器通常在不同的类中分组。
    • 出了点问题,我看到我没有调用 Convert(object value...) 函数。我应该在哪里称呼它?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2019-06-26
    • 2010-09-29
    • 2018-02-10
    相关资源
    最近更新 更多