WPF - 用不同的颜色显示 TextBlock
WPF - Display TextBlock with different colors
所以我想做的是显示一个 TextBlock,每行使用不同的颜色,理想情况下我想使用绑定。
我的 TextBlock 可能会显示一个项目列表,每个项目都有文本和颜色 属性。对于 foreach,我想在指定的颜色中使用 Texte 属性 为每个项目显示一行。
我已经尝试了以下方法:
1) 我制作了一个 TextBlock,其文本绑定到 ViewModel 中的字符串 属性,并且使用 foreach 我只是填充字符串,但在那种情况下无法在每一行上应用颜色,因为我想要。
2)我发现堆栈上的 有点帮助,建议使用 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" class,因为我认为这是误解的地方:
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)];
}
}
}
好吧,使用 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
指向主机 Window,但您可以将它绑定到您的 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):
<Window.Resources>
<local:ColoringConverter x:Key="coloringConverter" />
</Window.Resources>
所以我想做的是显示一个 TextBlock,每行使用不同的颜色,理想情况下我想使用绑定。
我的 TextBlock 可能会显示一个项目列表,每个项目都有文本和颜色 属性。对于 foreach,我想在指定的颜色中使用 Texte 属性 为每个项目显示一行。
我已经尝试了以下方法:
1) 我制作了一个 TextBlock,其文本绑定到 ViewModel 中的字符串 属性,并且使用 foreach 我只是填充字符串,但在那种情况下无法在每一行上应用颜色,因为我想要。
2)我发现堆栈上的 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" class,因为我认为这是误解的地方:
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)];
}
}
}
好吧,使用 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
指向主机 Window,但您可以将它绑定到您的 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):
<Window.Resources>
<local:ColoringConverter x:Key="coloringConverter" />
</Window.Resources>