Reputation: 1595
so what am trying to do is to display a TextBlock, using a different color for each line, and ideally I would like to use binding.
My TextBlock may display a list of Items, each Item has Texte and Color property. With a foreach, I want to display one line per item, with the Texte property, in specified Color.
I already tried the following :
1)I make a TextBlock, whose Text is binded to a string property in ViewModel, and with a foreach I just fill the string, but in that case cannot apply the colors on each line as I want.
2)I found that link on stack that helped a bit, where it is advised to use the Run
.
So in my ViewModel I fill the TextBlock like that :
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++;
}
}
My Model does the following, each time I need to update Legend I do :
contexte.UpdateLegend(MonGraphe.Legende);
this.CanvasLegend = contexte.Legende;
Then my View :
<Grid Grid.Column="2" Background="WhiteSmoke">
<TextBlock x:Name="CanvasLegend" TextAlignment="Left" HorizontalAlignment="Left"/>
</Grid>
For now it is not working at all and I cannot find why. I also would like to do everything in my ViewModel, but for that I would need to bind my Textlock to a TextBlock defined in my ViewModel, but cannot find how to do that?
EDIT :
As explained by Icebat I am using converter as the following :
XML identical to IceBat
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 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");
}
}
}
Also include my "Legende" class, as I think this is where there is missunderstanding :
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)];
}
}
}
Upvotes: 0
Views: 2387
Reputation: 4774
Well, the approach with Runs seems to be the simplest one. Not sure where you went wrong, but it all should be relatively easy. You can just use ItemsControl
for inlines:
<TextBlock x:Name="CanvasLegend" TextAlignment="Left" HorizontalAlignment="Left">
<TextBlock.Inlines>
<ItemsControl ItemsSource="{Binding LegendItems, ElementName=me,
Converter={StaticResource coloringConverter}}" />
</TextBlock.Inlines>
</TextBlock>
I use ElementName
to point to the host Window here for simplicity, but you can bind it to your ViewModel.
The converter looks like this:
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();
}
}
Then just add the converter in the resources somewhere (in my example it's Window):
<Window.Resources>
<local:ColoringConverter x:Key="coloringConverter" />
</Window.Resources>
Upvotes: 2