DataBinding Issue while using MultiDataTrigger - c#

Its looks simple and i tried all possible ways i know to fix the error still no luck, looks like am missing something.
Here is my code. at least the relevent part
<ItemsControl ItemsSource="{Binding Source}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal">
<ContentControl>
<Path x:Name="Bound" Stroke="Black">
<Path.Style>
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Condition1}"
Value="true"/>
<Condition Binding="{Binding Condition2}"
Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Path.Data">
<Setter.Value>
<RectangleGeometry Rect="{Binding Rect1}"/>
</Setter.Value>
</Setter>
<Setter Property="Path.Fill">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
// Here is the Problem
<TextBlock Text="{Binding Number}"
Width="50"
Height="30"
Background="White" />
// Binding is not working
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ContentControl>
</VirtualizingStackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
TextBlock in the visualBrush is not getting the value
'Number'
If i remove all the Triggers then everything work fine. somehow there is break in the binding.

That's because the VisualBrush doesn't have a DataContext. You have to use some proxy element.
Define your proxy element:
public class DataContextProxy: Freezable
{
public DataContextProxy()
{
BindingOperations.SetBinding(this, DataContextProperty, new Binding());
}
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
public static readonly DependencyProperty DataContextProperty = FrameworkElement
.DataContextProperty.AddOwner(typeof (DataContextProxy));
protected override Freezable CreateInstanceCore()
{
return new DataContextProxy();
}
}
Add it to some parent's Resources that has the DataContext you need:
<ItemsControl ItemsSource="{Binding Source}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal">
<ContentControl>
<ContentControl.Resources>
<behavior:DataContextProxy x:Key="Proxy"
DataContext="{Binding}" />
</ContentControl.Resources>
<Path x:Name="Bound" Stroke="Black">
...
And then bind your TextBlock's DataContext to the proxy's DataContext:
...
<Setter Property="Path.Fill">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
<TextBlock DataContext="{Binding Source={StaticResource Proxy},
Path=DataContext}"
Text="{Binding Number}"
Width="50"
Height="30"
Background="White" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
...
Haven't personally tried it, but should work... Comment if it doesn't!
Cheers.

Freezable objects likes VisualBrush is not part of the element tree(neither logical tree or VisualTree. so you have to get the datacontext and bind to Visual Property of VisualBrush.
Assuming number is from ViewModel Change your code as follows:
<TextBlock Text="{Binding Path=Number}" Width="50" Height="30" Background="White" />

Related

How to use trigger in datatemplate in WPF

How to change property Fill of icon of current selected element in checkbox depending on IsChecked property?
My resource dictionary:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- (...) -->
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="FooTemplate">
<Icons:ExIcon Fill="Red"
Width="12"
Height="Auto">
<!--
<Icons:ExIcon.Triggers>
<DataTrigger Binding="{Binding RelativeSource={???}}, Path=???}" Maybe here?
Value="???"/>
</Icons:ExIcon.Triggers>
-->
</Icons:ExIcon>
</DataTemplate>
<!-- (...) -->
<styles:IconSelector x:Key="IconSelector"
FooTemplate="{StaticResource FooTemplate}"
FooTemplateSSecond="{StaticResource FooTemaplteSecond}"/>
And listbox:
<ListBox ItemsSource="{Binding DataSources}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- (...) -->
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}">
<CheckBox.Template>
<!-- (...) -->
<ContentControl Name="Icon"
Content="{Binding}"
ContentTemplateSelector="{StaticResource IconSelector}"
HorizontalAlignment="Right"
Grid.Column="1"/>
<!-- (...) -->
</CheckBox.Template>
</CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Is it possible?
It's not completely clear to me how your CheckBox.Template looks like. Also the Icons:ExIcon Control is a mystery to me. Anyway here is a small working example. It uses a Rectangle instead of your ExIcon. But you can easily replace it ;-) I've bound directly to IsSelected via a DataTrigger instead of searching by ElementName or RelativeSource.
XAML:
<ListBox ItemsSource="{Binding}">
<ListBox.Resources>
<DataTemplate x:Key="template1">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}" />
<Rectangle Height="20"
Width="20">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill"
Value="Blue" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="True">
<Setter Property="Fill"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl ContentTemplate="{StaticResource template1}" Content="{Binding}">
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
NOTE Content="{Binding}". This sets the DataContext of the ContentPresenter to the "actual" DataContext of the ListBoxItem. If you use VS2015 you can investigate this with the VisualTreeViewer otherwise you can use https://snoopwpf.codeplex.com/
You should also consider to rename IsSelectedto IsChecked. Noramlly you use the IsSelected property for indicating if the row is selected or not.

ItemsControl with none background WPF

I would like to ask you, if is possible to have ItemsControl without background (x:null), not transparent.
I have collection with data, and these are showed in ItemsControl with help of DataTemplate. Some data in datatemplate are collapsed, and I need to be able to clickable on another control behind the itemscontrol.
Here is example what I mean:
<Button x:Name="bt_behind"></Button>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Class:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed">
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargeName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Example where item3 is partly collapsed and marked area, where is empty place
I set everywhere the background to null (try itemcontainerstyle too), but without success. On button behind ItemsControl still is not clickable. I think that ItemsControl have transparent background for events, but is it possible to remove this background?
Thanks for any advice and sorry for my english:)
-pav-
Well, as i said, it's all working. Fixed XAML:
<Grid>
<Button x:Name="bt_behind" Content="behind" Click="Bt_behind_OnClick"/>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargetName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Just for test.
private void Bt_behind_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("");
}

WPF DataGrid Cell binding by data from Parent control

I am trying to sort this out and cannot see light in the dark.
I have worksheets (tables) in workspace. All dynamic (columns, mount of tables, ...). Based on object for worksheet I would like to set datagrid rotation.
VM structure:
ControlVM :
{ Workspace: [
{
Worksheet: [
{
DataViewBindingData: [DataView type],
isRotated: rue | false
},
{...}
]
}
<ScrollViewer Grid.Column="0" Grid.Row="0" >
<ItemsControl ItemsSource="{Binding Path=Workspace}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Expander Header="{Binding Path=WorksheetName}" ExpandDirection="Down" IsExpanded="True" >
<StackPanel>
<DataGrid CanUserResizeColumns="False" HeadersVisibility="Column" CanUserSortColumns="False" HorizontalAlignment="Stretch" CanUserAddRows="False" Margin="5"
SelectionUnit="Cell" SelectionMode="Single" ItemsSource="{Binding Path=DataViewBindingData}" Loaded="DataGrid_Loaded"
BeginningEdit="DataGrid_BeginningEdit" />
</StackPanel>
</Expander>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
And inside custom styles resources in my base style for control (header, cells), I am trying to access isRotated property of worksheet object
<Style x:Key="DataGridBase" TargetType="Control">
<Style.Triggers>
<!-- TODO cannot find an ancestor for data binding -->
<DataTrigger Binding="{Binding IsRotated, Converter={StaticResource DebugConverter}, Mode=OneWay}" Value="True">
<Setter Property="LayoutTransform">
<Setter.Value>
<TransformGroup>
<RotateTransform Angle="-90" />
<ScaleTransform ScaleX="-1" ScaleY="1" />
</TransformGroup>
</Setter.Value>
</Setter>
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
</DataTrigger>
</Style.Triggers>
</Style >
<!--DataGrid Cells-->
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridBase}" />
And thats the problem, I dont know how to access parent control's data to bind the property. Thanks for suggestions.
The implicit DataContext inside DataGridCell is actually the current DataGridRow's bound item. So in this case you have to use RelativeSource to find the DataGrid and set Path to DataContext.IsRotated (the DataGrid's DataContext is a Worksheet):
<DataTrigger Binding="{Binding DataContext.IsRotated,
RelativeSource={RelativeSource AncestorType=DataGrid}
Converter={StaticResource DebugConverter}, Mode=OneWay}"
Value="True">
<!-- ... -->

DataBinding ListView creating components if a certain condition is met

I am working on a C# WPF project and I have a list view which uses data binding and a collection view.
Below is how my ListView is currently populated.
<ListView HorizontalAlignment="Left" Margin="547,12,0,41" Name="lstCallLogInformation" Width="320">
<ListView.Style>
<Style TargetType="ListView">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<TextBlock Text="No items in your devices call log" FontWeight="Bold" HorizontalAlignment="Center" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Foreground="Gray" Text="{Binding Name}" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Style="{StaticResource onmouseover}">
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal" Tag="{Binding logID}">
<Image Height="30" Source="{Binding base64ImageString, Converter={StaticResource ImageConverter}}" Width="30" />
<StackPanel Margin="10" Orientation="Vertical">
<TextBlock FontWeight="Bold" Text="{Binding contactNameOrPhoneNumber}" />
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Text="{Binding dateString}" />
<TextBlock Margin="15, 0, 0, 0" Text="Duration: " />
<TextBlock HorizontalAlignment="Right" Text="{Binding humanReadableCallDuration}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical">
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnAddAsContact" Click="btnAddAsContact_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/add.png" Tag="{Binding logID}" />
</Button>
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnDeleteContactFromLog" Click="btnDeleteContactFromLog_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/delete.png" Margin="0,0,0,5" Tag="{Binding logID}" />
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Within my DockPanel within the DataTemplate I have a StackPanel which contains various TextBlocks from the array that is passed into the binding.
In one of the StackPanels you will see you will see a TextBlock that binds to the element of contactNameOrPhoneNumber.
What I want to be able to do is if another value from the binding is null then I add a TextBlock for the contactNameOrPhoneNumber` element and alongside it have another TextBlock which contains another element of number``, if the other binding value is not null then this extra textblock of number is not added.
So in simple terms, what I want to be able to do is within the DataTemplate have a conditional if statement of if binding value == null add textblock to stackpanel else don't add textblock.
You can do that with Style + Triggers something like this:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding PropertyName}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Also you can create IValueConverter say ObjectToVisibilityConverter which will take input your property and if property is null return Visibility.Collapsed else return Visibility.Visible.
You can then bind property with Visibility value of TextBlock via Converter.
<TextBlock Visibility="{Binding PropertyName,
Converter={StaticResource ObjectToVisibilityConverter}}"/>

How to Bind ItemsSource of ListBox which is a Child of ItemsControl?

Let's say I have a View/Page something like below:
Category1 Category2 ......... Category(n)
Tile1 Tile1 Tile1
Tile2 Tile2 Tile2
..... ..... .....
..... ..... .....
Tile(n) Tile(n) Tile(n)
The XAML used to Create the above OutPut:
<ItemsControl ItemsSource="{Binding MenuCategories}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Title}" FontSize="30" />
<ListBox Grid.Row="1" x:Name="lst"
ItemsSource="{Binding ??????}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" MaxHeight="{Binding ElementName=lst, Path=ActualHeight}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Width" Value="250" />
<Setter Property="Height" Value="125" />
<Setter Property="Margin" Value="2.5" />
<Setter Property="Padding" Value="2.5" />
<Setter Property="Background" Value="{Binding DataContext.Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Converter={StaticResource stringToBrushConverter}}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="VerticalContentAlignment" Value="Bottom" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="{Binding DataContext.Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Converter ={StaticResource stringToBrushConverter}}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="125" Width="250">
<Path Data="{Binding DataContext.ImageData, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" VerticalAlignment="Center"
Stretch="Uniform" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
Width="68" Height="68" Margin="10" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
<TextBlock Text="{Binding Title, Converter={StaticResource spaceToNewLineConverter}}" VerticalAlignment="Top"
Margin="40,10,10,10" FontSize="24" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But the problem is that I don't know the binding of ItemsSource of the ListBox. So, I don't get the Output as shown above.
Here is how my database tables look like:
You can simply bind to Design_Master_Category that store collection of Design_Master_TileItems, for example :
......
<ListBox Grid.Row="1" x:Name="lst"
ItemsSource="{Binding Design_Master_TileItems} >
......
If you find that the property is empty at run-time as stated in comment, it means the problem is not at your binding. You need to fix your query. I am by no means well-experienced with EF, but here is my suggestion :
try to use .Include(o => o.Design_Master_TileItems) function when querying Design_Master_Category table. [Reference]

Categories