Change Text of Textblock in controltemplate - c#

I have a list of items which a user can select and I would like to add an explanation text. So far I have this code
<TabItem Name="TabItem02" Header="">
<ListBox Name="listBox01" VerticalAlignment="Stretch" SelectionMode="Multiple" HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedValuePath="Content">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<CheckBox Margin="5,2" IsChecked="{TemplateBinding IsSelected}" Grid.Column="0" VerticalAlignment="Top">
<ContentPresenter VerticalAlignment="Center">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Text="{TemplateBinding Content}" TextWrapping="Wrap" />
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</CheckBox>
<Expander Header="Explanation" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" FlowDirection="RightToLeft">
<TextBlock TextWrapping="Wrap" FlowDirection="LeftToRight"/>
</Expander>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBoxItem Name="List1Item01" IsEnabled="False"/>
<ListBoxItem Name="List1Item02" IsEnabled="False"/>
<ListBoxItem Name="List1Item03" IsEnabled="False"/>
<ListBoxItem Name="List1Item04" IsEnabled="False"/>
<ListBoxItem Name="List1Item05" IsEnabled="False"/>
</ListBox>
</TabItem>
And at the moment I am changing the content of each ListBox through a ressourcefile with this code-behind
foreach (ListBox listBox in ListBoxes)
{
foreach (ListBoxItem item in listBox.Items) item.Content = resourceManager.GetString(item.Name);
}
I would like to change the text of the second TextBlock (the one in the Expander) according to the ListBoxItem it belongs to as easy as the ListBox content itself through the ressourcefile. I would prefer not to create a separate Expander + TextBlock for each ListBoxItem with a separate name.
So if anyone has a solution for my problem, I would really appreciate that.

Related

WPF: Get control from ListView DataTemplate in Code Behind

I've created a ListView with a selfmade ItemTemplate, that is quite nested:
<ListView x:Name="QuestionListView" ItemsSource="{Binding QuestionList, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Background="#afafaf" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" CornerRadius="15,0,0,15" BorderBrush="Gray" BorderThickness="0,0,1,0" Background="#676767" />
<Border Grid.Column="1" BorderBrush="Gray" BorderThickness="0,3,1,3" Background="#8f8f8f" />
<Border Grid.Column="2" CornerRadius="0,15,15,0" BorderThickness="1" BorderBrush="Gray" Background="#676767" />
<Label Content="{Binding ID}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="40" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" Foreground="Beige" />
<!--This is the TextEditor, I need in my Code behind-->
<Viewbox Grid.Column="1">
<avalonEdit:TextEditor xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit" Name="textEditor" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" Width="600" FontSize="30" Background="Transparent" BorderBrush="Transparent" Foreground="Beige" PreviewLostKeyboardFocus="RichTextBox_PreviewLostKeyboardFocus" FontFamily="Consolas">
<i:Interaction.Behaviors>
<beh:AvalonEditBehaviour GiveMeTheText="{Binding TextQuestion, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</i:Interaction.Behaviors>
</avalonEdit:TextEditor>
</Viewbox>
<!--<TextBlock Grid.Column="1" IsHitTestVisible="False" FontSize="25" FontWeight="Bold" Foreground="LightGray" Text="Your question here" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding TextQuestion, Mode=TwoWay}" Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>-->
<StackPanel Orientation="Horizontal" Grid.Column="2" HorizontalAlignment="Center">
<Button Margin="10,0,5,0" Width="40" Height="40" Background="Gray" Command="{Binding EditCommand}" MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave">
<Image Source="{Binding EditImage}" />
</Button>
<Button Margin="5,0,10,0" Width="40" Height="40" Background="Gray" Command="{Binding DeleteCommand}">
<Image Source="{Binding DeleteImage}" />
</Button>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
Now I want to get the TextEditor textEditor in my code behind, because I need it for the "Intellisense" window, I am customizing.
So I have this in my Code behind:
public partial class QuestionListUC : UserControl
{
TextEditor editor;
public QuestionListUC()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
var template = QuestionListView.Template;
editor = (TextEditor)template.FindName("textEditor", QuestionListView);
}
}
But the FindName-Method outputs nothing for me, the variable "editor" is null.
What am I doing wrong here?
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => {
var template = QuestionListView.ItemTemplate;
editor = (TextEditor)template.FindName("textEditor",
QuestionListView);
}), DispatcherPriority.DataBind);
}
This should work
The most important change is
var template = QuestionListView.ItemTemplate;
The Dispatcher just delays the action until the data binding has been done, so the ItemTemplate should not be null
I managed to get a ViewBox around the Editor and then just use it's child. now I have the control in my CB

Remove hover effect on parent listbox item on hover of child listbox item WPF

I have started learning the WPF. I am facing the problem regarding hover effect on the list box.
I have used the list box control. Inside that control, I have added toggle button and On toggle button click I have bound the other list box.
Say,
Listbox -> Toggle button -> Listbox
I would like to remove parent (Name) list box item boarder hover effect when I hover over the title.
I have searched a lot but didn't found any hits how can remove parent hover effect from child element.
Here is the code that I have used for that.
<StackPanel.Resources>
<converters:BooleanToHiddenVisibility x:Key="boolToVis"/>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Items}" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemContainerStyle="{StaticResource NullSelectionStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel>
<ToggleButton Name="checkViewTextBox" >
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<TextBlock Name="Text" Text="{Binding Title}" HorizontalAlignment="Center" MinWidth="30"/>
</Grid>
<Grid Grid.Row="0" Grid.Column="1">
<Image Source="C:\Work\Temp_Olotech\ToggleButton\ToggleButton\Tick_Mark_Dark-512.png" Height="20" Width="20"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ListBox ItemsSource="{Binding InnerItem}" Visibility="{Binding Path=IsChecked, ElementName=checkViewTextBox,
Converter={StaticResource boolToVis}}" ScrollViewer.VerticalScrollBarVisibility="Auto" Height="200">
<ListBox.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel CanVerticallyScroll="True">
<Expander Header="Title">
<VirtualizingStackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
</VirtualizingStackPanel>
</Expander>
</VirtualizingStackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</VirtualizingStackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="only seen when above checkbox is checked"
Visibility="{Binding Path=IsChecked, ElementName=checkViewTextBox, Converter={StaticResource boolToVis}}"/>
</StackPanel>
</Grid>
Thank you for the help!

Is there a simple way to wrap text in a WPF ListBox?

I have searched around for this, found some solutions related to Winforms, and some even just saying it is really difficult in WPF to accomplish, but those posts are quite old.
If I have a standard ListBox, which is declared as:
<ListBox
x:Name="listBox"
HorizontalAlignment="Left"
Height="240"
Margin="401,68,0,0"
VerticalAlignment="Top"
Width="345"
SelectionChanged="listBox_SelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Grid.ColumnSpan="2"/>`
and programmatically:
System.ComponentModel.BindingList<string> listItems = new System.ComponentModel.BindingList<string>();
listBox.ItemsSource = listItems;
Is there a way for these strings to be wrapped within the ListBox?
Not hard at all:
<ListBox
....
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding}"
TextWrapping="Wrap"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you got StackPanel please get rid of them. They are bad with wrapping. Use GRID instead like #EdPlunkett suggested.
To use this:
<ItemsControl ItemsSource="{Binding MyErrors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Template="{StaticResource ErrorListContainerTemplate}"
ItemContainerStyle="{StaticResource ErrorListStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
Here is my style code:
<Style TargetType="{x:Type TextBlock}" x:Key="WrappingStyle">
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
</Style>
<Style TargetType="ContentPresenter" x:Key="ErrorListStyle">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TextBoxBorderErrorColor}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border Margin="0,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Grid.Column="0" Fill="{DynamicResource TextBoxBorderErrorColor}" VerticalAlignment="Top" HorizontalAlignment="Left">
<Path.Data>
<EllipseGeometry RadiusX="2.5" RadiusY="2.5"/>
</Path.Data>
</Path>
<ContentPresenter Grid.Column="1" Content="{Binding}" VerticalAlignment="Top">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource WrappingStyle}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

WPF TabItem HeaderTemplate

Sample code:
<TabControl>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal" Margin="5">
<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
<TextBlock Text="Blue" Foreground="Blue" />
</StackPanel>
</TabItem.Header>
<Label Content="Content goes here..." />
</TabItem>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="Red" Foreground="Red" />
</StackPanel>
</TabItem.Header>
</TabItem>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal" Margin="5">
<Rectangle Fill="Red" width="20" height="16" />
</StackPanel>
</TabItem.Header>
</TabItem>
</TabControl>
As you can see, the TabItem Header is Always a stackpanel with different content:
<TabItem.Header>
<StackPanel Orientation="Horizontal" Margin="5">
</StackPanel>
</TabItem.Header>
How can you put this in a template so that I don't have duplicate stackpanel code?
Trying to do it like this:
<TabControl>
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}">
</ContentPresenter>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem>
<TabItem.Header>
<!-- ??? -->
<TextBlock Text="X" />
<TextBlock Text="Y" />
</TabItem.Header>
</TabControl>
results in:
"The property 'Header' is set more than once."
"The object 'Object' already has a child and cannot add 'TextBlock'. 'Object' can accept only one child."
The only thing the tree headers really have common is the Margin="5". In second and third tab the stackpanel is irrelavant, because it has only one child. You can achive it either by using HeaderTemplate or ItemContainerStyle:
<TabControl>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Padding" Value="5" />
</Style>
</TabControl.ItemContainerStyle>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
<TextBlock Text="Blue" Foreground="Blue" />
</StackPanel>
</TabItem.Header>
<Label Content="Content goes here..." />
</TabItem>
<TabItem>
<TabItem.Header>
<TextBlock Text="Red" Foreground="Red" />
</TabItem.Header>
</TabItem>
<TabItem>
<TabItem.Header>
<Rectangle Fill="Red" Width="20" Height="16" />
</TabItem.Header>
</TabItem>
</TabControl>
now you don't repeat anything.
You could also extract the properties of stackpanel to style to avoid repeating them:
<TabItem.Header>
<StackPanel Style="{StaticResource TabHeaderPanel}">
<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
<TextBlock Text="Blue" Foreground="Blue" />
</StackPanel>
</TabItem.Header>
<TabItem.Header>
<StackPanel Style="{StaticResource TabHeaderPanel}">
<TextBlock Text="Red" Foreground="Red" />
</StackPanel>
</TabItem.Header>
<TabItem.Header>
<StackPanel Style="{StaticResource TabHeaderPanel}">
<Rectangle Fill="Red" width="20" height="16" />
</StackPanel>
</TabItem.Header>
If you want even more code reuse, you should consider MVVM-like approach:
<TabControl ItemsSource="{Binding Tabs}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="local:TabViewModel">
<StackPanel Orientation="Horizontal" Margin="5">
<Image x:Name="Icon" Source="{Binding Icon, Converter={StaticResource UriToBitmapSourceConverter}}" />
<Rectangle x:Name="ColorRect" Height="16" Width="16" Fill="{Binding Color}" Visibility="Collapsed" />
<TextBlock Text="{Binding Title}" Foreground="{Binding Color}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Icon}" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
<Setter TargetName="ColorRect" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
if you can't use single DataTemplate for all headers, you can use HeaderTemplateSelector
I think HeaderTemplate will only be used if you are binding the Tab collection and generating them dynamically. This would work if you set your objects in a ViewModel (Header property is a collection of items that with implicit DataTemplates, could generate TextBlocks or Rectangles depending on their types).
Otherwise, you would either need to give your StackPanels common styling to make them all identical and save repetition, or set the first element of the TabItem.Header content to a custom control that derives from StackPanel.
I would choose the Style route myself, unless there is a really pressing need to remove even the StackPanel line from your XAML.

How to use wpf expender alignment

I am trying to create a WPF window to have a simple expander to extend the datagrid when the user clicks on the More expander button. When the user wants to hide the datagrid, user just has to clik on the Less expander button.
I am also using dock panel to separate header, left, right and footer.
The problems are :
I want to have the less button to be in center before the user
click on the more expander button. When the user clicks on the more
expander button, the less button will be pushed to the left, to show
the datagrid on the right.
How do I change the name of the expander when its closed and
open. Can I do it at the xaml level?
Below is the xaml code:
<Window x:Class="M.SalesWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SalesWindow" Height="300" Width="300">
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<Label FontSize="28" Content="Sales">
</Label>
</StackPanel>
<StackPanel DockPanel.Dock="Left" Width="auto" HorizontalAlignment="Center">
<Label FontSize="15" Content="Enter Amount" Height="26" Width="168" />
<Separator Width="168" />
</StackPanel>
<StackPanel DockPanel.Dock="Right">
<Expander ExpandDirection="Left" HorizontalAlignment="Right" VerticalAlignment="Stretch">
<Expander.Header>
<TextBlock Text="More">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
<Expander.Content>
<StackPanel>
<DataGrid ItemsSource="{Binding Products}">
</DataGrid>
</StackPanel>
</Expander.Content>
</Expander>
</StackPanel>
</DockPanel>
</Window>
Thank you.
Replace your code with the below code and see the magic. This will also solve your problem of alignment as per your requirement. I have removed the DockPanel as you can achieve the similar result with this piece of code.
<StackPanel>
<StackPanel>
<Label FontSize="28" Content="Sales">
</Label>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" HorizontalAlignment="Center">
<Label FontSize="15" Content="Enter Amount" Height="26" Width="168" />
<Separator Width="168" />
</StackPanel>
<Expander Grid.Column="1" ExpandDirection="Left" HorizontalAlignment="Right" VerticalAlignment="Stretch">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="IsExpanded" Value="False" />
<Setter Property="Header">
<Setter.Value>
<TextBlock Text="Less">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded,RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Header">
<Setter.Value>
<TextBlock Text="More">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Content>
<StackPanel>
<DataGrid ItemsSource="{Binding Products}">
</DataGrid>
</StackPanel>
</Expander.Content>
</Expander>
</Grid>
</StackPanel>
The trick lies under the following lines of code.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

Categories

Resources