Reputation: 5420
how do i write following code as XAML
Assuming we have this snip of code
var myValue = someMethodeReturn(); // the return will fit
// could also be an other nonbindable property
var myTextBlock = new TextBlock();
myTextBlock.Inlines = myValue;
how would you convert
var myValue = someMethodeReturn();
and
myTextBlock.Inlines = myValue;
as XAML ONLY
surely the first part could look like ???={Binding myProperty}
and the secound part like <TextBlock.Inlines><???/></TextBlock.Inlines>
but what would ???
be look like ?
the visual result should be something like this (if you execute youe solution)
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="LightGray" TextWrapping="WrapWithOverflow" >
<TextBlock.Inlines>
<Run>meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run 1</Run>
<Run Foreground="Green" FontFamily='Palatino Linotype' Typography.Variants='Superscript'>meine run2</Run>
<Run>meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run</Run>
<Run Foreground="LimeGreen" Background="Yellow">meine run3</Run>
<Run>meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run</Run>
</TextBlock.Inlines>
</TextBlock>
i tested ItemsControl
between the TextBlock.Inlines
Tages but it returns TextBlocks as default and i wasn't able to set Run
or InLine
as ItemTemplate
i think i just need a Control
that returns a List<Inline>
but i doesn't know which Control
will be able to do this
any suggestions would be appreciated
First
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="LightGray" TextWrapping="WrapWithOverflow" >
<TextBlock.Inlines>
<ContentControl Content="{Binding myBinding}"/>
</TextBlock.Inlines>
</TextBlock>
returns just "(Auflistung)" as Text
Secound
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="LightGray" TextWrapping="WrapWithOverflow" >
<TextBlock.Inlines>
<ItemsControl ItemsSource="{Binding myBinding}"/>
</TextBlock.Inlines>
</TextBlock>
returns a List of TextBoxes wrapped in a ContenPresenter
Upvotes: 2
Views: 473
Reputation: 4606
Create bindable Inlines
attached property as follows:
Bindable.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace WpfApplication
{
public static class Bindable
{
public static readonly DependencyProperty InlinesProperty = DependencyProperty.RegisterAttached("Inlines", typeof(IEnumerable<Inline>), typeof(Bindable), new PropertyMetadata(OnInlinesChanged));
private static void OnInlinesChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var textBlock = source as TextBlock;
if (textBlock != null)
{
textBlock.Inlines.Clear();
var inlines = e.NewValue as IEnumerable<Inline>;
if (inlines != null)
textBlock.Inlines.AddRange(inlines);
}
}
[AttachedPropertyBrowsableForType(typeof(TextBlock))]
public static IEnumerable<Inline> GetInlines(this TextBlock textBlock)
{
return (IEnumerable<Inline>)textBlock.GetValue(InlinesProperty);
}
public static void SetInlines(this TextBlock textBlock, IEnumerable<Inline> inlines)
{
textBlock.SetValue(InlinesProperty, inlines);
}
}
}
And use it like this:
MyViewModel.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApplication
{
public class MyViewModel
{
// This is against MVVM principle - to contain views (Inlines) in view model, but I don't want to complicate by creating ViewModel class for each Inline derived class.
public IEnumerable<Inline> Inlines { get; private set; }
public MyViewModel()
{
this.Inlines = new Inline[]
{
new Run("meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run 1"),
new Run("meine run2") { Foreground = Brushes.Green, Typography = { Variants = FontVariants.Superscript } },
new Run("meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run"),
new Run("meine run3") { Foreground = Brushes.LimeGreen, Background = Brushes.Yellow },
new Run("meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run")
};
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication">
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<TextBlock local:Bindable.Inlines="{Binding Inlines}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="LightGray" TextWrapping="WrapWithOverflow"/>
</Window>
Upvotes: 1
Reputation: 12319
My idea would have been to use an ItemsControl and set the ItemPanel to WrapPanel, then insert TextBlocks or ContentPresenters for each item. Turns out those TextBlocks will not wrap as nicely as I'd have expected:
Well, you at least have a ViewModel, so how about putting your text items through the meat grinder before committing them to your source collection? Looks right afterwards:
Now if it would only feel right as well ;)! Maybe you can use some of it, at least.
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<MyRunModel> _myRuns = new ObservableCollection<MyRunModel>();
public ObservableCollection<MyRunModel> MyRuns { get { return _myRuns; } set { _myRuns = value; OnPropertyChanged("MyRuns"); } }
public ViewModel()
{
List<MyRunModel> runs = new List<MyRunModel>();
runs.Add(new MyRunModel() { Text = "Meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run1" });
runs.Add(new MyRunModel() { Text = "Meine run2", Foreground = ForegroundDescription.HighlightDark });
runs.Add(new MyRunModel() { Text = "Meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run3" });
runs.Add(new MyRunModel() { Text = "Meine run4", Foreground = ForegroundDescription.HighlightLight, Background = BackgroundDescription.Highlight });
runs.Add(new MyRunModel() { Text = "Meine sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr sehr lange run5" });
CommitMyRuns(runs);
}
/// <summary>
/// Splits up every run into words (delimited by space), and adds the parts
/// to the collection that can be bound to the UI. Retains formatting information.
/// </summary>
/// <param name="runs"></param>
private void CommitMyRuns(List<MyRunModel> runs)
{
int runCount = runs.Count;
for (int i = 0; i < runCount; i++)
{
string[] parts = runs[i].Text.Split(' ');
int partCount = parts.Length;
for (int j = 0; j < partCount; j++)
{
bool isLast = j == parts.Length - 1;
MyRunModel run = new MyRunModel()
{
Text = parts[j] + (isLast ? string.Empty : " "), // add space that was lost in split
Foreground = runs[i].Foreground, // keep formatting
Background = runs[i].Background
};
MyRuns.Add(run);
}
MyRuns.Add(new MyRunModel() { Text = " " }); // add a space after each of the original runs (unformatted)
}
}
}
public class MyRunModel
{
public string Text { get; set; }
// do not use UI types (e.g. Brush) directly in viewmodel
public ForegroundDescription Foreground { get; set; }
public BackgroundDescription Background { get; set; }
}
public enum ForegroundDescription
{
None = 0,
HighlightDark,
HighlightLight
}
public enum BackgroundDescription
{
None = 0,
Highlight
}
Xaml:
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<SolidColorBrush x:Key="ForegroundHighlightDarkBrush" Color="Green" />
<SolidColorBrush x:Key="ForegroundHighlightLightBrush" Color="LimeGreen" />
<SolidColorBrush x:Key="BackgroundHighlightBrush" Color="Yellow" />
</Window.Resources>
<Grid>
<TextBlock>
<TextBlock.Inlines>
<ItemsControl ItemsSource="{Binding MyRuns}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyRunModel}">
<TextBlock x:Name="presenter" TextWrapping="Wrap" Text="{Binding Text}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Foreground}" Value="HighlightDark">
<Setter TargetName="presenter" Property="TextElement.Foreground" Value="{StaticResource ForegroundHighlightDarkBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Foreground}" Value="HighlightLight">
<Setter TargetName="presenter" Property="TextElement.Foreground" Value="{StaticResource ForegroundHighlightLightBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Background}" Value="Highlight">
<Setter TargetName="presenter" Property="TextElement.Background" Value="{StaticResource BackgroundHighlightBrush}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</TextBlock.Inlines>
</TextBlock>
</Grid>
Subclassing the textbox to make the Inlines bindable might be a possibility. See this answer.
Upvotes: 1
Reputation: 3035
To add a string value you can use
myTextBlock.Inlines.Add(new Run(myValue));
If myValue is an array loop through and add multiple run elements
Msdn docs for Run class http://msdn.microsoft.com/en-us/library/system.windows.documents.run.aspx
Upvotes: 1