WPF listbox. Skip underscore symbols in strings - c#

I have some WPF ListBox which is dynamically populating with items. Something like this :
ListBox.Items.Add
(new ListBoxItem { Content = new CheckBox { IsChecked = true, Content = "string_string"} );
The problem is with checkbox content. It's showing on GUI like "stringstring"...
How to escape the "_" symbols ? (I get strings dynamically)

You can add the text in a TextBlock and put that TextBlock inside your Chekbox, TextBlock does not support _ mnemonic characters. Here's what I mean, in xaml, but you can easily convert this to code:
<CheckBox IsChecked="True">
<TextBlock>string_string</TextBlock>
</CheckBox>

The default template for the CheckBox contains a ContentPresenter whose RecognizesAccessKey is set to true. If the content is a string (which it is in your case), then the ContentPresenter creates an AccessText element to display the text. That element hides the underscore until the Alt key is pressed because it will treat it as a mnemonic. You can either retemplate the CheckBox such that its ContentPresenter's RecognizesAccessKey is false or better yet just provide a DataTemplate as the ContentTemplate which contains a TextBlock. If you're not sure if the content will be a string then you can set the ContentTemplateSelector and in code provide a DataTemplate which contains a TextBlock only if the item is a string. e.g.
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox.Resources>
<DataTemplate DataType="sys:String" x:Key="stringTemplate">
<TextBlock Text="{Binding}" />
</DataTemplate>
<Style TargetType="CheckBox">
<Setter Property="ContentTemplate" Value="{StaticResource stringTemplate}" />
</Style>
</ListBox.Resources>
<ListBoxItem>
<CheckBox Content="A_B" ContentTemplate="{StaticResource stringTemplate}"/>
<!-- Or use the implicit style to set the ContentTemplate -->
<CheckBox Content="A_B" />
</ListBoxItem>
</ListBox>

Use a double underscore string__string, since in WPF, the _ is the mnemonic character.
Even better, just solve this issue in xaml and create a collection in your view model (or code-behind).

I had the same problem in a DataGrid. Similarly to AndrewS, I added a style for TextBlock, but did not use DataTemplate or ContentTemplate. This way the setters in ColumnHeaderStyle were applied :) However, this solution works only for single underscores, eg. "a_b", but not for "a__b".
<DataGrid>
<DataGrid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding}"/>
</Style>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<!-- my setters here do not get overridden -->
</DataGrid.ColumnHeaderStyle>
</DataGrid>

Related

How to update a DataTemplate dynamically based on a object property?

I have a collection of objects that have different rendering options: They can be simple text, editable text, comboboxes, or event a mixed bag (like a comboBox where items are usually text but with images for specific values).
I managed to show everything correctly by using ContentTemplateSelector inside a ContentPresenter node inside the DataTemplate of the ListViewItem.ItemTemplate:
<ContentPresenter
Grid.Row="1"
Grid.Column="1"
Content="{Binding .}"
ContentTemplateSelector="{DynamicResource ResourceKey=RenderTemplateSelector}"/>
(note that everything is inside the DataTemplate of a ListView.ItemTemplate)
All is good until I change the value of said property and need to change the template, for instance going from a image to a text.
The VM does update correctly the values, but nothing happens in the GUI.
Looking around here there are a few methods (using a Converter (bound to which property??), defining a Style (i think is not relevant, since i must show different controls, not change properties of the same one), using an AttachedProperty (I don 't understand really well how attached properties work, but from what I saw I should have a ContentControl node, which I don't...) but nothing seems what I need.
So, a recap: I have a ListView that has a DataTemplate definded for each ListViewItem that contains another DataTemplate that needs to change based on a properti of the ListViewItem object. I managed to achieve this with a ContentTemplateSelector, but that is assigned at the beginning and then never changed.
You could try to replace the ContentPresenter with a ContentControl that has a Style with data triggers that sets the ContentTemplate, .e.g.:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="Type1">
<Setter Property="ContentTemplate" Value="{StaticResource template1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="Type2">
<Setter Property="ContentTemplate" Value="{StaticResource template2}" />
</DataTrigger>
<!-- ... -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

XAML Binding path to variable

I need to bind the text property of a text box to an item stored in a Dictionary and I have to get it by dictonary's key name , but the problem is that the name is binding in the Tag property and I have no more information since it is built by a static resource into a style
This would be what I want to do:
In the code behind:
Dictionary<string,string> filters = new Dictionary...... ;
In XAML
<Style TargetType="{x:Type DataGridColumnHeader}" x:Key="StyleName">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Tag="{Binding}" Text="{Binding Path=filters[Tag]}", Mode= TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
In this case Tag in "filter[Tag]" is not a dictionary's key but a property Tag of Textbox.
Does somebody know if it is possible to do that? or another way to build. this?
Thanks.

TabControl - ToggleButtons as TabSeletors (XAML-Only Logic)

Currently I have a set of ToggleButtons.
I would like to show a different Tab of my TabControl depending on which button is checked. Basically the same bahaviour like when a differnet Tab is selected. Not sure if my needs are nonsense but anyways. I want the SelectedTab to change depending on which button is clicked. Moreover my ToggleButtons are RadioButtons stlyed to Togglebuttons (I only want one to be checked at a time). I want to try to achieve my needs only in XAML (if even possible).
So here's part of my XAML:
<Window.Resources>
<sys:Int32 x:Key="CurrentTab"></sys:Int32>
<Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Aqua"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
</Window.Resources>
<TabControl Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
SelectedIndex="{StaticResource CurrentTab}">
<TabItem Visibility="Collapsed"></TabItem>
<TabItem Visibility="Collapsed"></TabItem>
</TabControl>
What I was thinking of would be something like (pseudoCode):
<Setter Target="{StaticResource CurrentTab}" Value="{ButtonsToolTip}></Setter>
Basically is it even possible to assign values to variables in XAML and if it is - how ?
As an example on why and what I try to achieve is something like this GUI:
You cannot change value of a primitive type declared as resource using xaml. But you can use a property of an object to act as your variable. Eg;
<sys:Int32 x:Key="IntKey">12</sys:Int32>
is non-modifiable using XAML. But, Value property of DictionaryEntry (shown below) is modifiable, despite the fact that like int(IntKey), DEKey is non-modifiable too.
<coll:DictionaryEntry x:Key="DEKey" Key="TagKey" Value="100"/>
If I try to change integer(IntKey) via binding , it won't allow. Eg; <TextBox Text="{Binding Mode=OneWay,Source={StaticResource IntKey}}"/> , Mode must be OneWay. TwoWay, OneWayToSource values are not allowed.
But I can write
<TextBox Text="{Binding Value,Source={StaticResource DEKey}}"/>
and any textbox value will be updated in Value of DictionaryEntry(DEKey). Note, two-way binding won't work as DictionaryEntry is not a DependencyObject. But you can now change your variable (Value property) the way you like. But only concern is : changes to Value property will not be reflected back in bounded control.
Yes, you can make use of above information to show Tabs w.r.t. radiobuttons with approach given below. For binding to work properly both ways, we need a DependencyObject and a DependencyProperty, so we use FrameworkElement and its Tag property.
This Tag property now mimics your Variable in question.
<Window.Resources>
<FrameworkElement x:Key="rbTagHolder" Tag="0"/>
</Window.Resources>
...
<ItemsControl x:Name="RadioButtonList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding TabName}" Tag="{Binding TagValue}" GroupName="Choice">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{DynamicResource rbTagHolder}" PropertyName="Tag" Value="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type RadioButton} }}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
...
<TabControl x:Name="TabCtrl" SelectedIndex="{Binding Tag, Source={StaticResource rbTagHolder}}"> ... </TabControl>
Code-behind
RadioButtonList.ItemsSource = new[] { new { TabName = "Tab1", TagValue = "0" }, new { TabName = "Tab2", TagValue = "1" },
new { TabName = "Tab3", TagValue = "2" }, new { TabName = "Tab4", TagValue = "3" }};
Just in case you don't know how to use Blend Behaviors.
A. Include following namespaces :
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
B. Add references to : Microsoft.Expression.Interactions, System.Windows.Interactivity On my system these are found in
C:\Program Files (x86)\Microsoft SDKs\Expression\Blend.NETFramework\v4.0\Libraries
There is no way to change the value of a StaticResource with an Event trigger in XAML alone. This will have to be done by binding your StaticResource to a ViewModel's property or using code behind.

ListView.SelectedItems does not change when clicking a TextBox in the ItemTemplate

I have a ListView bound with an ObservableCollection. The Itemtemplate of the ListView is the following:
<ListView ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" PreviewMouseLeftButtonUp="ListViewClicked" PreviewKeyDown="NameBox_KeyDown">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/DeleteIcon.png"/>
<TextBox Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way I give the user the choice to either change the name of the element, or delete it from the list. I handle this two operations in code behind through the two event handlers attached to the ListView.
The problem is that when I click on the TextBox, the SelectedItems property of the ListView doesn't change and it points the last selected item. When I click on the image or on the free space around the selection change.
Now I have two questions:
Is there an easy way to fix this behavior?
Is it possible to get a reference to the collection item whose property is exposed by the TextBox?
One way to deal with this problem is to set Trigger to set IsSelected when keyboard focus is within ListViewItem
<ListView ... SelectionMode="Single">
<!-- .... -->
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>

how to set the generated item for itemscontrol in databinding?

if I'm using a ListBox for data binding, the listbox generates a listboxitem for each item, same goes for combo box and comboBoxItem.
My question is - how do i set it myself for a given ItemsControl? (e.g. make the containing element be Border)?
The default item used to wrap each item is a ContentPresenter
I am not sure why you'd want to overwrite this, since it has no visual appearance or specific behavior that would mess with your UI.
You can set the ItemTemplate if you want to wrap each item in a Border object
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Blue" BorderThickness="2">
<TextBlock Text="{Binding }" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
Or set the ItemContainerStyle if you want to apply any specific style to the ContentPresenter
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
After digging a little with ILSpy -
Apparently The magic is done in
protected override DependencyObject GetContainerForItemOverride()
{
return new ListBoxItem();
}
This is where ListBox does it - and in my control I should override this as well.

Categories

Resources