I am using standard Visual Studio templates and I have a ItemsDetailPage that contains a FlipView with a RichTextBlock in its DataTemplate.
I want to set the RichTextBlock block to my custom Paragraphs generated in text. I think there is no way to bind RichTextBlocks Block in XAML so I am using code behind. In the Loaded event of RichTextBlock I set its Block, that works ok. But the problem is, the Loaded event gets called only once when the page is displayed. When I "flip" to another item, the selected item of the FlipView changes but the Loaded event does not get called again (I think this is ok).
I tried setting the RichTextBlock in the FlipViews SelectionChanged item but that does not work.
var ind = this.flipView.SelectedIndex;
var flipViewItem = this.flipView.ItemContainerGenerator.ContainerFromIndex(flipView.SelectedIndex);
if (flipViewItem != null)
{
var scroller = FindFirstElementInVisualTree<ScrollViewer>(flipViewItem);
var tb = scroller.FindDescendantByName("richTextColumns").FindDescendantByName("richTextBlock") as RichTextBlock;
SetRichContent(tb, (flipView.SelectedItem as ArticleViewModel).HtmlContent);
}
The SetRichContent gets called, sets the RichTextBlocks Blocks but visually they do not change and after a few flips, the whole app crashes without any additional information.
So my question is, how do I get my own code called on the RichTextBlock with each flip (seleced item change)?
You can bind rich text boxes. Make sure your data context is set properly. We need to see more code to make an appropriate answer.
<RichTextColumns>
<RichTextColumns.ColumnTemplate>
<DataTemplate>
<RichTextBlockOverflow Width="400" Margin="50,0,0,0"/>
</DataTemplate>
</RichTextColumns.ColumnTemplate>
<RichTextBlock Width="400">
<Paragraph>
<Run Text="{Binding Content}"/>
</Paragraph>
</RichTextBlock>
</RichTextColumns>
Related
I'm using XAML Islands to make my app and I want to use Windows 10 styling in my WPF app like here. For example <TextBlock Text="Header" Style="{StaticResource HeaderTextBlockStyle}"/> would result in:
But this doesn't work in WPF (It does work in UWP without any modifications), my understanding is that XAML Islands should make it possible. When I try to simply add the code above to my xaml file I get the exception
Cannot find resource named 'HeaderTextBlockStyle'. Resource names are case sensitive.
I get the same exception if I add Style="{StaticResource HeaderTextBlockStyle}" to a <xamlhost:WindowsXamlHost> element.
So I tried to add the controls with code so I added this WindowsXamlHost control as a stackpanel:
<xamlhost:WindowsXamlHost InitialTypeName="Windows.UI.Xaml.Controls.StackPanel" ChildChanged="WindowsXamlHost_ChildChanged"/>
And added this method (an event handler that is ran when the control is made. Learned it from this) that handles adding additional controls (a TextBlock) to the StackPanel:
private void WindowsXamlHost_ChildChanged(object sender, EventArgs e)
{
// Get the host control
WindowsXamlHost host = (WindowsXamlHost)sender;
// Get the StackPanel in the host
Windows.UI.Xaml.Controls.StackPanel sp = (Windows.UI.Xaml.Controls.StackPanel)host.Child;
// Make a TextBlock to add to the StackPanel
Windows.UI.Xaml.Controls.TextBlock textBlock = new Windows.UI.Xaml.Controls.TextBlock();
// Set the text of the TextBlock
textBlock.Text = "LockCursorInMonitor";
// Get the style resources, cast them to the appropriate type for XAML Islands and add them to the TextBlock
textBlock.Style = (Windows.UI.Xaml.Style)Application.Current.Resources["HeaderTextBlockStyle"];
// Another way to get resources but this doesn't work too.
//textBlock.Style = (Windows.UI.Xaml.Style)this.FindResource("HeaderTextBlockStyle");
// Add the TextBlock to the stackpanel
sp.Children.Add(textBlock);
}
The Application.Current.Resources["HeaderTextBlockStyle"] way does nothing and doesn't throw an exception.
The this.FindResource("HeaderTextBlockStyle") way throws the next exception:
System.Windows.ResourceReferenceKeyNotFoundException: ''HeaderTextBlockStyle' resource not found.'
So how can I get these style resources in my WPF app?
One way to achieve this is to use the package ModernWPF but then you lose all the benefits of XAML Islands (if there are any. Everything I needed from XAML Islands is in ModernWPF and is easier to implement).
After installing and setting ModernWPF up you can simply use the <TextBlock Text="Header" Style="{StaticResource HeaderTextBlockStyle}"/> way and it just works.
I have an xaml datagrid template in my WPF application that binds/pulls data from a sharepoint site. Each row has a button with a tooltip and the text is dynamically loaded from this connection. When a user clicked on the button, it would copy the tooltip text to the Windows ClipBoard. Just recently, my existing code behind stopped working, and can't seem to get that dynamically loaded tooltip text anymore to work with in code. I've ruled out the clipboard as I can manually set the text and it copies to the clipboard and I can still see the dynamically loaded text in the UI so it can't be the connection. Below is how I use to get the text in the click event:
var buttonTemplate = ((Button)sender).ToolTip;
var buttonTTtext = ((TextBlock)buttonTemplate).Text;
System.Windows.Clipboard.SetText(buttonTTtext.ToString());
Here is the xaml I'm using in the datagrid template:
<mui:ModernButton Click="someButton_Click" Tag="{Binding Path=MyTemplate}" >
<mui:ModernButton.ToolTip>
<TextBlock Text="{Binding Path=template}" TextWrapping="Wrap" />
</mui:ModernButton.ToolTip>
</mui:ModernButton>
Thanks for the help...the tooltip text was in the DataContext of the button. I solved my issue with this code:
Button btn = (Button)sender;
var dc = btn.DataContext.GetType().GetProperty("template").GetValue(btn.DataContext, null);
Hi this should be faily simple, however I don't know what I am doing wrong. I've been looking all over the internet seeing people make this work, even followed the tutorial on MSDN still nothing has worked for me.
I want to Iterate over a ListBox, and get the ListBoxItems so I can find the DataTemplate that I have added to it.
This is my code behind.
private void SetListBoxDataTemplate(ListBox MyListBox)
{
try
{
foreach (CustomDataTemplateObject dataobject in MyListBox.Items)
{
ListBoxItem lbi = (ListBoxItem)(MyListBox.ItemContainerGenerator.ContainerFromItem(dataobject));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(lbi);
DataTemplate dt = myContentPresenter.ContentTemplate;
TextBlock tb = (TextBlock)dt.FindName("ListBoxItemTextBlock1", myContentPresenter);
ComboBox cb = (ComboBox)dt.FindName("ListBoxItemComboBox1", myContentPresenter);
tb.Text = dataobject.Text;
cb.ItemsSource = dataobject.ListColors;
}
}
catch (Exception ex)
{
MessageBox.Show(""+ex);
}
}
XAML looks like this:
<DataTemplate x:Key="ListBoxItemDataTemplate1">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
<TextBlock Name="ListBoxItemTextBlock1" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
</TextBlock>
</Border>
<ComboBox Name="ListBoxItemComboBox1" />
</StackPanel>
</DataTemplate>*
<StackPanel>
<ListBox Name="ListBoxTest1" ItemTemplate="{DynamicResource ListBoxItemDataTemplate1}" />
</StackPanel>
I have tried with setting my itemtemplate to static to see if it works, and the method i'm calling from code behind, is called after I have populated my ListBoxs
My dataobject is NOT null, however when i call the line in my code behind, my lbi, ends up being null.
Any suggestions? thanks in advance!
FIRST UPDATE
This problem only occurs if i call the method in my constructor, so perhaps it's because it hasn't initialized the full group element section yet. However I want to do this as soon as possible. Am I perhaps forced to do it in a WindowLoaded event?
SECOND UPDATE
Code updated, Rachel's answer worked for iterating over my ListBoxItems, however the Listbox Has not fully rendered since i'm unable to reach the Datatemplate at this time. So MyListBox_GeneratorStatusChanged is not working for this problem, but it does get the ListBoxItems.
WPF's main thread runs items at different priority levels. Code that runs in the Constructor all gets run at Normal priority, while things like rendering the ListBox and it's items run at the Render priority level, which occurs after all Normal priority operations have finished.
This means that your entire Constructor gets run (including SetListBoxDataTemplate()) before your ListBox is even rendered and the items get generated.
If you want to run some code after the items are generated, use the ItemsContainerGenerator.StatusChanged event
// Constructor
MyListBox.ItemContainerGenerator.StatusChanged += MyListBox_GeneratorStatusChanged;
...
void MyListBox_GeneratorStatusChanged(object sender, EventArgs e)
{
// return if containers have not been generated yet
if (MyListBox.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
// remove event
MyListBox.ItemContainerGenerator.StatusChanged -= MyListBox_GeneratorStatusChanged;
// your items are now generated
SetListBoxDataTemplate(MyListBox);
}
What are you trying to accomplish with this method anyways? It is a bit unusual for WPF, and there may be a much better WPF way of accomplishing your task.
Updated based on new code added to Question
A much better method of setting your Text and ItemsSource properties is to make use of WPF's data bindings.
Your DataTemplate should look like this:
<DataTemplate x:Key="ListBoxItemDataTemplate1">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1 1 0 1" MinWidth="50">
<TextBlock Text="{Binding Text}" Background="{Binding ElementName=ListBoxItemComboBox1, Path=SelectedValue}" >
</TextBlock>
</Border>
<ComboBox ItemsSource="{Binding ListColors}" />
</StackPanel>
</DataTemplate>*
A DataTemplate is like a cookie cutter. It's used to make the UI objects, but is not part of the UI object itself. All it does is tell WPF that "When you go to render this object, render it using this XAML". So the way your XAML gets rendered is
<ListBoxItem>
<StackPanel>
<Border>
<TextBlock Text="{Binding Text}" />
</Border>
<ComboBox ItemsSource="{Binding ListColors}">
</StackPanel>
</ListBoxItem>
In addition, the DataContext behind your ListBoxItem is the item from the collection bound to ListBox.ItemsSource, which based on your code should be CustomDataTemplateObject. That allows the bindings from the DataTemplate to work
If you're new to WPF and struggling to understand how exact the DataContext works, I'd recommend reading this article of mine: What is this "DataContext" you speak of?.
To summarize, WPF has two layers to an application: the UI layer and the Data Layer (DataContext). When you perform a basic binding like above, you are pulling data from the data layer into the UI layer.
So your ListBoxItem has a data layer of CustomDataTemplateObject, and the TextBlock.Text and ComboBox.ItemsSource bindings are pulling data from the data layer for use in the UI layer.
I'd also highly recommend using a utility like Snoop which lets you view the entire Visual Tree of a running WPF application to see how items get rendered. Its very useful for debugging or learning more about how WPF works.
You're confusing two jobs and mixing them into one. First, get access to the ListBoxItem:
private void SetListBoxDataTemplate(ListBox MyListBox)
{
foreach (ListBoxItem listBoxItem in MyListBox.Items)
{
}
}
Now you can get the DataTemplate from the ListBoxItem:
foreach (ListBoxItem listBoxItem in MyListBox.Items)
{
ContentPresenter presenter = FindVisualChild<ContentPresenter>(listBoxItem);
DataTemplate dataTemplate = presenter.ContentTemplate;
if (dataTemplate != null)
{
// Do something with dataTemplate here
}
}
The FindVisualChild method can be found in the How to: Find DataTemplate-Generated Elements page on MSDN.
UPDATE >>>
To answer your edit, yes, the constructor will be too early to try to access these DataTemplates because the Framework won't have applied them to all of the objects by then. It is best to use the FrameworkElement.Loaded Event to do these kinds of things, as that is the first event that can be called after the controls have all been initialised.
I'm using a telerik RadGridView which is pretty much the same thing as a normal DataGrid in WPF. In my gridview.columns I have a GridViewDataColumn which then allows me to put a celltemplate then a datatemplate and then allow me to put different controls within a grid. I have a combobox and a textbox(only one shows at a time based on visibility property). The problem I'm having is the tab system is kind of weird and doesn't work right. When I tab to a cell in the column above, my combobox nor my textbox ever gets focus. In fact the cell turns completly white. So I was wondering how (in code behind) can I detect when a user tabs in this particular cell and manually set focus to these child elements inside this cell on the selected row?
<telerik:GridViewDataColumn x:Name="MyDataColumn" Focusable="True" GotFocus="MyDataColumn_GotFocus_1" Header="Header1" Width="250">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Textbox x:name="MyTextbox" Visibility="{Binding IsTextbox}"/>
<Combobox x:name="MyCombobox" Visibility="{Binding IsCombo}"/>
</Grid>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewDataColumn>
*Basically, how can I gain access to one of those child controls inside this GridViewDataColumn in code behind so that I can set focus to it? Thanks so much for any advice.
Probably the most straight forward answer to your question can be found by reading the answer to the Access items inside the DataTemplate in WPF post.
However, it may be worth reading the correct answer in this Access Elements inside a DataTemplate… How to for more than 1 DataTemplate? post also.
I have a chat window where each message is a TextBlock. I want to be able to select the text inside my TextBlocks. Google says to use a TextBox instead, which I cannot do because they do not support runs, which I am using to create hyperlinks inside my messages. What options do I have?
Check out the RichTextBox implementation for WPF.
How about making an CustomContentControl with TextBox (with selection style) and a Link in it and play with visibility based on content. Some thing like
<myControls:CustomContentControl>
<Grid>
// play with visibilty depending on content.
<CustomLink/>
<TextBox Style="{StaticResource SelectionStyle}"/>
</Grid>
</myControls:ContentControl>