How to bind a template element property in DataTemplate - c#

I want to let a template element property create a binding with another element property, but I found a lot articles and none of them talk about. So I ask for what should I do.
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:StringsJoinConverter x:Key="join_converter" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
<ListBox ItemsSource="{Binding Collection}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Background="#FF83C9A9" >
<TextBlock Text="Name:"> </TextBlock>
<TextBox Width="50" x:Name="input_name"></TextBox>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Background="#FFB683C9" >
<TextBlock Text="Value:"> </TextBlock>
<TextBox Width="50" x:Name="input_value"></TextBox>
</TextBlock>
</StackPanel>
<TextBlock >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource join_converter}">
<Binding>
<!--Bind input_name.Text-->
</Binding>
<Binding>
<!--Bind input_value.Text-->
</Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
enter image description here

You can directly bind CompplexProperty.PartA and PartB also you can use ElementName method, In the
below code you can see the both ways also.
<TextBlock >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource join_converter}">
<Binding ElementName="ATextblock" Path="Text">
</Binding>
<Binding Path="ComplexProperty.PartB">
</Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

Related

How to simplify verbose WPF DataGrid Xaml?

I have a WPF DataGrid with many columns (below I showed only 3 columns for simplicity). To mark up a single column it takes almost 20 lines of code and it repeats for every column. The only difference between the columns is the header text and the binding.
MainView.xaml:
<Window x:Class="UserInterface.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Converters="clr-namespace:UserInterface.Converters"
xmlns:UiUtilities="clr-namespace:.UserInterface.UIUtilities"
mc:Ignorable="d"
Icon="Application.ico"
Title="{Binding WindowTitle}"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
DataContext="{Binding MainViewModel}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<Converters:VisibleIfOfTypeXConverter x:Key="VisibleIfOfTypeX" />
</Window.Resources>
<DataGrid ItemsSource="{Binding DataModel}" HeadersVisibility="Column" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Header1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="UserInterface:SomeType">
<Grid>
<TextBox Visibility="{Binding Converter={StaticResource VisibleIfOfTypeX}}">
<TextBox.Text>
<Binding Path="P1Text" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<UiUtilities:NumberRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Header2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="UserInterface:SomeType">
<Grid>
<TextBox Visibility="{Binding Converter={StaticResource VisibleIfOfTypeX}}">
<TextBox.Text>
<Binding Path="P2Text" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<UiUtilities:NumberRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Header3">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="UserInterface:SomeType">
<Grid> <TextBox Visibility="{Binding Converter={StaticResource VisibleIfOfTypeX}}">
<TextBox.Text>
<Binding Path="P3Text" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<UiUtilities:NumberRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
I'm still a beginner in WPF, but I assume there must be a less verbose way to implement this. I imagine there must be some sort of "templating" or components as they are used in other UI Frameworks.
Could we create some sort of component (I call it "MyCustomDataGridColumn"), that we could use like this:
<DataGrid ItemsSource="{Binding DataModel}">
<DataGrid.Columns>
<MyCustomDataGridColumn Header="Header1" TextBoxBinding="P1Text">
<MyCustomDataGridColumn Header="Header2" TextBoxBinding="P2Text">
<MyCustomDataGridColumn Header="Header3" TextBoxBinding="P3Text">
</DataGrid.Columns>
</DataGrid>
How could we achieve this? Or are there other/better ways to reduce verbosity?

Adding a label to an ItemsControl

Visual presentation is not my strong side when it comes to programming. I'm developing a small program for my company, ment to aid in a schedule overview (can also be described as a timeline).
The background works work as expected, so I don't include this code here as it's not a question of why it's not displaying this data or so. My questions are more on how to present it visually better.
The timeline shows up as a gray bar and is then populated with buttons on the timeline where an event is taking place.
I would like your advice on how I can:
Add a label to the left of the gray bar.
Add a visual timeline (i.e. timestamps) as a gradient above the gray bar, so it visually presents where in the timeline is 3 o'clock and where is 6 o'clock.
or, i would appriciate some advice on where I can read more about it, a hint in the right direction.
Thank you!
<Window x:Class="Test.BookingOverview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="Bokningsöversikt" Height="450" Width="800">
<Window.Resources>
<local:EventLengthConverter x:Key="mEventLengthConverter"/>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=TimeLines}" Margin="10,90,10,27" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid x:Name="EventContainer" Height="20" Margin="5" Background="Gainsboro">
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Grid.Column="1" Background="Yellow" VerticalAlignment="Stretch" HorizontalAlignment="Left">
<Button.Margin>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding ElementName="TimeLine" Path="DataContext.Duration"/>
<Binding Path="Start"/>
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Button.Margin>
<Button.Width>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding ElementName="TimeLine" Path="DataContext.Duration"/>
<Binding Path="Duration"/>
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Button.Width>
<Button.Content>
<TextBlock Text="{Binding customer}"></TextBlock>
</Button.Content>
<Button.ContextMenu>
<ContextMenu Name="Test">
<MenuItem Header="Testar contextmenu"></MenuItem>
<Separator></Separator>
<MenuItem Header="Testar igen"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
<Button.ToolTip>
<ToolTip Content="Testar"></ToolTip>
</Button.ToolTip>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Aepot got me in the right direction, but currently it's creating rows of labeling and then the acctual timelines. Most likely due to something I din't understand by using ItemsControls.
Changes made to the code according to the suggestion of Aepot:
<StackPanel Orientation="Vertical" Margin="90,123,0,0">
<TextBlock Text="Bokningsöversikt"></TextBlock>
<ItemsControl ItemsSource="{Binding Path=TimeLines}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid x:Name="EventContainer" Grid.Column="2" Height="20" Margin="5" Background="Gainsboro"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="10" Margin="0,0,0,0" Padding="10,0,0,0" Grid.Column="1" Background="Transparent" VerticalAlignment="Center" HorizontalAlignment="Left">
<TextBlock.Text>
<Binding ElementName="EventContainer" Path="DataContext.RegNr"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=TimeLines}" >
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding RegNr}"/>
<Separator></Separator>
<MenuItem Header="Test"></MenuItem>
</ContextMenu>
</ItemsControl.ContextMenu>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="TimeLine" ItemsSource="{Binding Path=Events}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid x:Name="EventContainer" Grid.Column="2" Height="20" Margin="5" Background="Gainsboro"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Grid.Column="2" Background="Yellow" VerticalAlignment="Stretch" HorizontalAlignment="Left">
<Button.Margin>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding ElementName="TimeLine" Path="DataContext.Duration"/>
<Binding Path="Start"/>
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Button.Margin>
<Button.Width>
<MultiBinding Converter="{StaticResource mEventLengthConverter}">
<Binding ElementName="TimeLine" Path="DataContext.Duration"/>
<Binding Path="Duration"/>
<Binding ElementName="EventContainer" Path="ActualWidth"/>
</MultiBinding>
</Button.Width>
<Button.Content>
<TextBlock Text="{Binding customer}"></TextBlock>
</Button.Content>
<Button.ContextMenu>
<ContextMenu Name="Test">
<MenuItem Header="Testar contextmenu"></MenuItem>
<Separator></Separator>
<MenuItem Header="Testar igen"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
<Button.ToolTip>
<ToolTip Content="Testar"></ToolTip>
</Button.ToolTip>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Note: use TextBlock instead of Label in case of displaying text because it's better optimized for that.
Try wrapping the controls with StackPanel.
<DockPanel>
<TextBlock Text="My timeline"/>
<StackPanel Orientation="Vertical">
<ItemsControl .../><!-- with TextBlocks for labeling timeline -->
<ItemsControl .../><!-- existing one -->
</StackPanel>
</DockPanel>
You can also use StackPanel as Control to use in ItemsPanelTemplate.
Or this way, more friendly if you're familiar with HTML <table>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.RowSpan="2" Text="My timeline"/>
<ItemsControl Grid.Column="1" .../><!-- with TextBlocks for labeling timeline -->
<ItemsControl Grid.Column="1" Grid.Row="1" .../><!-- existing one -->
</Grid>

SelectedItem changes textblock variables

I want to get all the data from an Object used as ObservableCollection in a ListView. The ListView is working properly I want to when the Item is selected the information in a TextBlock (outside the ListView) to update with the info that the object as.
How can I do it? What I'm doing wrong?
So to the code:
<ListView
x:Name="dataGrid"
ItemsSource="{Binding Friends}"
Height="586"
BorderThickness="0"
SelectedItem="{Binding SelectedItemFriends,Mode=TwoWay}"
HorizontalAlignment="Left" Width="460">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Resources\Images\ic_status.png" Height="24" Width="18"/>
<StackPanel Margin="5" Orientation="Vertical">
<TextBlock FontWeight="Bold" Text="{Binding name}"/>
<StackPanel x:Name="RemoveItems" Margin="5" Orientation="Vertical">
<TextBlock Text="{Binding lastLocation, StringFormat='Location: {0}'}"/>
<TextBlock Text="{Binding timestamp}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel x:Name="AdditionItems" Margin="5" Orientation="Vertical" Visibility="Visible">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myConverter}">
<Binding ElementName="dataGrid" Path="loc.country"/>
<Binding ElementName="dataGrid" Path="loc.area"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
You should specify SelectedItem property in your binding:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myConverter}">
<Binding ElementName="dataGrid" Path="SelectedItem.loc.country"/>
<Binding ElementName="dataGrid" Path="SelectedItem.loc.area"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

Can't get rid of red validation border around listbox control (IDataErrorInfo)

I have removed red border from styling properties but it still remains on listbox, how can I fix this?
As you can see no red border is this a default thing?
<Window.Resources>
<ControlTemplate x:Key="eTemplate">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Foreground="Black" FontSize="14" Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" >
</TextBlock>
<Border BorderThickness="0">
<AdornedElementPlaceholder x:Name="adorned"/>
</Border>
</DockPanel>
</ControlTemplate>
</Window.Resources>
.
<ListBox HorizontalAlignment="Left" Height="73" Margin="449,275,0,0" VerticalAlignment="Top" Width="94" Name="LocationTo" SelectionChanged="listBox_SelectionChanged">
<ListBox.SelectedIndex>
<Binding Path="LocationTo" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"/>
</ListBox.SelectedIndex>
<ListBoxItem Content="Conn Junction"/>
<ListBoxItem Content="Multhy Pass"/>
<ListBoxItem Content="Suddean Halt"/>
<ListBoxItem Content="End Terminal"/>
</ListBox>
.
<TextBox Height="27" Validation.ErrorTemplate="{StaticResource ResourceKey=eTemplate}" HorizontalAlignment="Left" Margin="88,62,0,0" Name="FName" VerticalAlignment="Top" Width="127" FontSize="12" TextChanged="FName_TextChanged">
<TextBox.Text>
<Binding Path="FName" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>

Binding to a CollectionViewSource returns DependencyProperty.UnsetValue

I need to make an item checked whether it is in use or not by a member of a collection.
When I bind directly to the collection (binding 2) it does work but not through a view of it (binding 3), it returns DependencyProperty.UnsetValue.
Why isn't it working ?
<ListBox.ItemTemplate>
<DataTemplate DataType="system:String">
<StackPanel Orientation="Horizontal">
<CheckBox>
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource AssetToBooleanConverter}" Mode="OneWay" >
<Binding />
<Binding Path="Assets" Source="{StaticResource Singleton}" />
<Binding Source="{StaticResource CvsAssets}" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
<TextBlock
Text="{Binding Converter={StaticResource PathToNameConverter}}"
ToolTip="{Binding Converter={StaticResource PathToSizeConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Here is the view of that collection :
<CollectionViewSource x:Key="CvsAssets"
IsLiveSortingRequested="True"
Source="{Binding Source={StaticResource Singleton},
Path=Assets}"
d:IsDataSource="True">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription Converter="{StaticResource ObjectToTypeNameConverter}" />
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="Name" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
I've just fixed it using the following syntax :
<Binding Source="{StaticResource CvsAssets}" Path="(CollectionView.SourceCollection)" />
(Debugger gave a warning about the binding and the type which was ListCollectionView)

Categories

Resources