I was working on a project recently where I feel a nice solution would be to display a paragraph in a RichTextBox that is bound to a collection of objects that will hold a string and a few properties about that string. I was hoping I could do something like this:
<RichTextBox Name="rtb" IsReadOnly="True" FontSize="14" FontFamily="Courier New">
<FlowDocument>
<Paragraph>
<Run Foreground="Red" Text="{Binding foo}"/>
<Run Foreground="Green" Text="{Binding bar}"/>
<Run Foreground="Blue" Text="{Binding baz}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
This works fine, however this is defining all the bindings ahead of time, but I actually won't know them until run time, each string will be stored into a collection. I was trying to think of a way to define and bind multiple Run blocks and place them all in the RichTextBox to be displayed. I would prefer a primarily xaml solution if possible, but if I must do it in the code behind that may be OK as well as long as the bindings stick and everything updates properly after the initial load.
I tried something like this, but was having trouble figuring how how to bind to the Text property of a Run from the code behind:
foreach (var item in col)
{
Run run = new Run();
Binding b = new Binding();
b.Source = this;
b.Path = new PropertyPath(item.StaticText);
run.SetBinding(run.Text, b); //Tells me Text is not a dependency property
//But it could be bound if this were xaml?
}
I also tried putting an ItemsControl inside the RichTextBox that had Run as the data template of the items, but it wouldn't allow me to make the data template a Run tag, and I'm not sure that would have worked anywway.
Any guidance on the best approach, or what's wrong with my solution.
You could use this...
<Paragraph>
<ItemsControl ItemsSource="{Binding MyRuns}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Foreground="{Binding Color}" Text="{Binding Text}"/>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate >
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--<Run Foreground="Red" Text="{Binding foo}"/>
<Run Foreground="Green" Text="{Binding bar}"/>
<Run Foreground="Blue" Text="{Binding baz}"/>-->
</Paragraph>
And define MyRuns as a collection on your view model, e.g.
public ObservableCollection<RunData> MyRuns { get; set; }
Put properties in your RunData etc.
And you can of course remove ItemsControl.ItemsPanel, that's just for fun
This seems to have gotten the job done for me, I created an ItemsControl that used a WrapPanel as the ItemsPanel. Then I can bind each item to my collection of strings and it comes out looking like a nice paragraph of variable strings. I'll keep the question open though in case someone comes up with a more elegant solution.
<RichTextBox Name="rtb" IsReadOnly="True">
<FlowDocument>
<Paragraph>
<ItemsControl ItemsSource="{Binding col}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding StaticText,
UpdateSourceTrigger=PropertyChanged}"/><TextBlock Text=" "/>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Paragraph>
</FlowDocument>
</RichTextBox>
Related
I have a ListBox in my WPF MVVM app using the following code:
<GroupBox Grid.Column="0" Margin="0,0,0,-58">
<DockPanel>
<TextBlock DockPanel.Dock="Top"
HorizontalAlignment="Center"
Margin="0,0,0,8"
FontWeight="Bold"
Text="{x:Static p:Resources.AvaliableLEDsLabel}" />
<ListBox Name="AvailableLEDsListbox" SelectionMode="Extended"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}"
ItemTemplate="{StaticResource DataTemplateListBoxItem}"
ItemsSource="{Binding AvailableLeds}"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
>
<ListBox.GroupStyle>
<StaticResource ResourceKey="StyleListBoxGroup" />
</ListBox.GroupStyle>
</ListBox>
</DockPanel>
</GroupBox>
This displays grouped lists of devices, with LEDs under them. The DataTemplate is the following:
<GroupStyle x:Key="StyleListBoxGroup">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding HideGroupCommand}">X</Button>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Left"
FontWeight="Bold"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
<DataTemplate x:Key="DataTemplateListBoxItem">
<TextBlock x:Name="LedId" Text="{Binding LedId}"/>
</DataTemplate>
I would like to make the X button in the header hooked up to the HideGroupCommand toggle the hiding of all the items under that particular header. How would I go about doing this? Thanks in advance.
You have few options.
First one :
You would need to have a property in your view model something like 'ListBoxVisibility' then u would bind that property to your UI. In command text u just changed visibility property of that property in view model- so u have it reflected on UI. This visibility property can be of type 'bool' , or 'Visibility' or whatever. Only if it's type of Visibility u don't need converter when binding.
NOTE : Some people use it - even though it kinda goes out of general principel of MVVM patter. But sometimes u have to do it.
Second
If wanna stick to MVVM , then u need to fully separate your UI from your viewmodel. Create click event and change visibility.
I've defined a for a listbox item, and binded the text inside the tooltip to two properties of the object (name + description) but i have an issue that the text is being cut off
here is my tooltip:
<ToolTipService.ToolTip>
<StackPanel >
<StackPanel Orientation="Vertical">
<TextBlock FontSize="13">
<Bold>Name</Bold>
</TextBlock>
<TextBlock Text="{x:Bind name}"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock FontSize="13" TextWrapping="Wrap">
<Bold>Description</Bold>
</TextBlock>
<TextBlock Text="{x:Bind description}"/>
</StackPanel>
</StackPanel>
</ToolTipService.ToolTip>
Now the thing is, if i bind the tooltip to a method that returns the name + description (Which is how it was previously, but was super ugly) it does show all the text, it was like this:
<ToolTipService.ToolTip>
<TextBlock Text="{x:Bind Description}"/>
</ToolTipService.ToolTip>
But I needed to style it to make it look better, so i've tried to do what was posted above.
I've already tried setting the Width/Height to super large values, didn't do anything.
any ideas?
The tooltip template probably has a default maximum width, which cuts off the TextBlock. To solve this, just add TextWrapping attribute:
<TextBlock TextWrapping="Wrap" Text="{x:Bind description}"/>
Now the tooltip text will wrap on multiple lines as necessary
I have this template (c# windows univeral app w8.1)
<ListView ItemsSource="{Binding SelectedSongStanzas}">
<ListView.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Verses}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Left">
<TextBlock Padding="0" Text="{Binding Harmonization2}" FontFamily="Lucida Console" FontSize="16" Foreground="BlueViolet" VerticalAlignment="Bottom"/>
<TextBlock Padding="0" Text="{Binding OriginalText}" FontFamily="Lucida Console" FontSize="16" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And This is how it look
If see at image the first list view item shows the first ListItem lines are together with no spaces between them, but the second line shows a big gap between them.
Note:
1) I'm ,was trying with Grid instead stackpanel but looks same way.
2) If Try fixed height texts could be cut.
What can i do to force all ListItems looks the same way?
(Just Like the firs Item)
EDIT ONE: Based on the comments think the problem could be the default styles from windows universal app template. And now could be another question.
How to edit / change / Override those styles on that template ?
Finally as #AnjumSKhan post, the problem wasn't at XAML code, just my structure Has a Literal char \n
Something like this "some text here" was stored as => "\nsome text here", just i remove those new lines literals an now all looks as must be.
I'm using a WrapPanel as a part of an ItemsControl in a WP8 application. everything works fine including it's designtime data view, and I'm using the same ViewModel with a WinRT application which uses a WrapGrid (which works both designtime and runtime)
While all the other elements of the View show up on runtime perfectly, the Itemscontrol does not populate, is there any known issue with the WrapPanel, how do I show a gridlike view of my items then? the code I've used is below
<ItemsControl x:Name="times" Background="{x:Null}" Height="120" ItemsSource="{Binding CheckinTimes}" ItemsPanel="{StaticResource BoxStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="5,10" Width="70" >
<TextBlock Text="{Binding Type}" Margin="0" Foreground="White" FontFamily="Segoe UI Light" FontSize="13.333" HorizontalAlignment="Left" />
<TextBlock Text="{Binding Time}" Margin="0" Foreground="White" FontFamily="Segoe UI Light" FontSize="13.333" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
under resources
<ItemsPanelTemplate x:Key="BoxStyle">
<toolkit:WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
I was hiding the control before populating it, but the visibility was wrongly bound. I corrected that and it fixed this issue, I'm not sure how it's related but it worked. I would say for any similar errors, watch the bindings around control visibility.
Here is the XAML
<ListView x:Name="duplicateVarsInDepXMLListView" ItemsSource="{Binding}" HorizontalAlignment="Left" Height="100" Margin="362,360,0,0" VerticalAlignment="Top" Width="306">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name, StringFormat='Variable Name: {0}'}"/>
<TextBlock Text="{Binding Value,StringFormat='Value: {0}'}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This xaml displays a List of collection with Variables Names and the values of a collection. We have something like Foreground property to change the colour of the binding characters or FontWeight to make it Bold.
However i only want to Change the font of the Label e.g. VariableName and not to the actual binding.
Is this possible to achieve? Any suggestions
You can use Run elements inside a TextBlock and apply different formatting to different runs. You can bind to the FontFamily property of the Run to dynamically set the font:
<TextBlock>
<Run Text="Variable Name: " FontFamily="{Binding FontName}"/>
<Run Text="{Binding Name}"/>
</TextBlock>
If the text in the TextBlock is wrapped using TextWrapping="Wrap" the runs will wrap around as expected. This does not apply to the question but can make using Run elements superior to stacking TextBlock elements in a StackPanel.
Extract out seperate TextBlock and set FontFamily on that TextBlock. Wrap both TextBlocks in StackPanel and set the orientation to Horizontal -
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Variable Name: " FontFamily="Comic Sans MS"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<TextBlock Text="{Binding Value,StringFormat='Value: {0}'}"/>
</StackPanel>
You could do something like:
<StackPanel Orientation=Horizontal>
<TextBlock Text="Variable Name: "/>
<TextBlock Text={Binding Name} FontWeight="Bold"/>
</StackPanel>