Why is the binding failing within a ItemsControl - wpf

I created an Index property on the object in the list the ItemsControl's ItemsSource is bound to, but when I put a breakpoint in my converter I see the value for that binding is DependancyProperty.UnsetValue. The data context for the ContentPresenter is that object, there is a property on the object, why doesn't it see the Index property?
<ItemsControl ItemsSource="{Binding Charts}" x:Name="ItemsControl">
<ItemsControl.ItemTemplate>
<ItemContainerTemplate >
<ContentPresenter Content="{Binding}">
<ContentPresenter.Visibility>
<MultiBinding Converter="{StaticResource Converter}">
<Binding Path="Index"/>
<Binding Path="WhichAreVisible" />
</MultiBinding>
</ContentPresenter.Visibility>
</ContentPresenter>
</ItemContainerTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

ItemTemplate is the wrong place to do that. That's going to be a DataTemplate or ItemContainerTemplate used to display the content of the items in the ItemsControl, within some other child container control -- one per child. That other container control is what you want to hide, not just the content within it. Of course if it's auto-sized with no padding or margin, it won't take up any space once the content collapses, but then you're counting on that remaining the case.
Try this and see what you get; ItemContainerStyle controls the style of the actual child item. In a ListBox, it would be applied to the ListBoxItem type; in an ItemsControl, the children are ContentPresenters. If you already have an ItemContainerStyle, just add this trigger to it.
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource Converter}">
<Binding Path="Index"/>
<Binding Path="WhichAreVisible" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>

Related

How to make this ItemsControl into StaticResource

I have an ItemsControl which renders some Point into apropriately positioned ellipses similar to small dots in a 2D map.
Since my screen contains drawings where the same ItemsControl must be displayed many times, I tried to make it into StaticResource, but two things were wrong:
When I try to instantiate the same resource multiple times, the second times give an error. I have read in other answer that this is because StaticResources are, well, static, and you cannot have two instances of a static Control in the Visual Tree at the same time;
Whe I instantiate only one, the Element Binding to PainelMarc.ActualHeight (for example) does not work;
So my goal is to DRY up my XAML by converting this ItemsControl, or parts of it, into reusable resources.
<ItemsControl x:Name="PainelMarc"
ItemsSource="{Binding ExameAtivo.ListaMarcadores, Mode=TwoWay}" Grid.Row="1" Grid.RowSpan="3">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type FrameworkElement}">
<Setter Property="RenderTransform">
<Setter.Value>
<MultiBinding Converter="{StaticResource MarcadoresConverter}">
<Binding />
<Binding ElementName="PainelMarc" Path="ActualHeight"/>
<Binding ElementName="PainelMarc" Path="ActualWidth"/>
<Binding Source="{StaticResource LimitesFrontal}" Path="Geometry.Bounds"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Double">
<Canvas>
<Ellipse x:Name="elipsemouseover"
Width="10"
Height="{Binding Width, RelativeSource={RelativeSource Self}}"
Fill="White" Stroke="Black" StrokeThickness="1" RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TranslateTransform X="-5" Y="-5"/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Set x:Shared="False" on your resource so that everytime resource lookup is done via StaticResource, it returns new instance of resource.
Default value is true for all resources. Hence, you are getting the error regarding addition of same control in different Visual Trees.
From MSDN link of x:Shared:
When set to false, modifies WPF resource-retrieval behavior so that
requests for the attributed resource create a new instance for each
request instead of sharing the same instance for all requests.
And for second question that ElementName binding not working. That should work fine, I see no issue in that code. Your converter should fire successfully.
Just in case it doesn't, you can try with RelativeSource in place of ElementName to get ItemsControl:
<Binding RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=ItemsControl}" Path="ActualHeight"/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=ItemsControl}" Path="ActualWidth"/>

Conditional Formatting of items in a Combox

Currently I'm in a team building a datagrid that contains data coming from a viewmodel. We are using reactive-ui and each "row" in the datagrid is represented by a viewmodel class that inherits from "ReactiveObject".
There are 2 properties in the viewmodel tha tI'm interested in to fill a combobox in each row. A collection of PossibleScales and the DefaultScale. The PossibleScales are loaded into the combobox and the DefaultScale is used to set the selecteditem to the DefaultScale. The DefaultScale is always contained by the DefaultScale collection. What I want to achieve is that any non-default scale in the combobox is formatted in italics. So I created a style that invokes a IMultiValueConverter. so, I need to give this IMultiValueConverter 2 parameters, the current scale (from the collection of PossibleScales and the DefaultScale.
Here's the XAML that we have to fill the combobox with all the items in the list of PossibleScales (I have the same for EditingElementStyle):
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=PossibleScales}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource Measeurementscale}"></Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
The Measeurementscale StaticResource is defined as a style in the resources section of the control.
<Style x:Key="Measeurementscale" TargetType="ComboBoxItem">
<Setter Property="FontStyle">
<Setter.Value>
<MultiBinding Converter="{StaticResource NonDefaultScaleToItalicConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="DataContext"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
As you can see, the current value of the comboboxitem is passed to the IMultiValueConverter, so that's great. But now I'm wondering how I can pass the DefaultScale (which is a property of the viewmodel and defined as DataContext for the control) to this converter as well. I have not been able to do this.
Any help would be appreciated.
You can use FindAncestor to get the parent control where this data context is set, and bind to it. For example:
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ComboBox}"
Path="DataContext.DefaultScale" />

WPF XamComboBox DisplayMemberPath with concatenated values

I have the following code -
<igEditors:XamComboEditor ItemsSource="{Binding Instances}"
Margin="5,2,5,2" Width="175" HorizontalAlignment="Left"
SelectedItem="{Binding SelectedInstance,Mode=TwoWay,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnExceptions=True}"
>
<igEditors:XamComboEditor.ComboBoxStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path="Name" />
<Binding Path="Id" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</igEditors:XamComboEditor.ComboBoxStyle>
</igEditors:XamComboEditor>
When I set the SelectedInstance from my viewmodel, the combobox is displaying the type of the object. If I then make a selection, it displays correctly, but I click out of the combobox, losing focus, it reverts back to the object type. If I set the DisplayMemberPath manually to just Name, it works correctly, but I really need it to be a concatenated value for the displaymemberpath.
Can anyone help?
Thanks
The answer to this question was to use the ValueToDisplayTextConverter along with a custom converter. More details can be found here -
http://www.infragistics.com/community/forums/p/77378/390782.aspx

XamDataGrid CellValuPresenter tooltip style

I am trying to create a style for my XamDataGrid so that I can determine what values to show in a tooltip based on the current cell that is being hovered over.
I am trying the below:
<Style x:Key="MyCVPStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel>
<ListView ItemsSource="{Binding RelativeSource={...}, Path=Field, Converter={StaticResource MyFieldConverter}}">
...
The DataContext of the style is set to the DataRecord. The problem I am having is that I do not know how to access the actual field of the cell value presenter.
I tried setting the source to:
{Binding RelativeSource={RelativeSource AncestorType={x:Type igDP:CellValuePresenter}}, Path=Field
but that fails with a binding error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Infragistics.Windows.DataPresenter.CellValuePresenter', AncestorLevel='1''. BindingExpression:Path=Field; DataItem=null; target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
How can I access/bind to the CellValuePresenter.Field so I can pass that value to my converter?
As a reference, I have other CellValuePresenter styles that work without issue where I access the Field. For example, here the second binding param is the Field, accessed by referencing Self:
<Setter Property="BorderThickness">
<Setter.Value>
<MultiBinding Converter="{StaticResource BorderThicknessConverter}">
<MultiBinding.Bindings>
<Binding Path="DataItem" />
<Binding RelativeSource="{RelativeSource Self}" Path="Field" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
You could use the PlacementTarget property of ToolTip, which will be the CellValuePresenter, and set it as the DataContext that the ListView is binding to:
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<StackPanel>
<ListView ItemsSource="{Binding Path=Field, Converter={StaticResource MyFieldConverter}}"></ListView>
</StackPanel>
</ToolTip>
</Setter.Value>
</Setter>
I found an alternate solution that I feel is more straight forward.
Create the following style (I believe the property you are trying to bind ItemsSource to is named Field)
<Style x:Key="MyCVPStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel>
<ListView ItemsSource="{Binding Path=Cells[Field].Value}" />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
But this does require that you have the field you're binding your ItemsSource to defined in FieldLayout.Fields. You may want to set the Visibility of that field to collapsed. I've also included the field that has the tooltip style applied to it.
<igDP:Field Label="Value" Name="Value" >
<igDP:Field.Settings>
<igDP:FieldSettings CellValuePresenterStyle="{StaticResource ResourceKey=MyCVPStyle}" />
</igDP:Field.Settings>
</igDP:Field>
<igDP:Field Label="Field" Name="Field" Visibility="Collapsed" />
You could use a different style for the different fields within the XamDataGrid so that each style would already be set up for the specific field.
The reason why the binding is failing is because the tooltip is shown in a pop up so it is another window and not in the same visual tree as the CellValuePresenter.

Including objects binding in MultiBinding command parameter

I am attempting to use a <MultiBinding> to pass two parameters to my ViewModel's command but am having issues getting the XAML parser to accept my attempts.
Consider the following ListView contained within my UserControl which is bound to a collection of Ticket objects.
<ListView x:Name="lvTicketSummaries"
ItemsSource="{Binding TicketSummaries}"
ItemContainerStyle="{DynamicResource ResourceKey=ListViewItem}"
IsSynchronizedWithCurrentItem="True">
The respective style ListViewItem
<Style x:Key="ListViewItem" TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{DynamicResource ResourceKey=cmListViewItem}"/>
<!-- A load of irrelevant stuff ->
</Style>
And the referenced ContextMenu item where the source of my query sits;
<!-- ContextMenu DataContext bound to UserControls view model so it can access 'Agents' ObservableCollection -->
<ContextMenu x:Key="cmListViewItem" DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext}">
<MenuItem Header="Send as Notification" ItemsSource="{Binding Path=Agents}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<!-- Display the name of the agent (this works) -->
<Setter Property="Header" Value="{Binding Path=Name}"/>
<!-- Set the command to that of one on usercontrols viewmodel (this works) -->
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.SendTicketNotification}" />
<Setter Property="CommandParameter">
<Setter.Value>
<MultiBinding Converter="{StaticResource ResourceKey=TicketNotificationParameterConverter}">
<MultiBinding.Bindings>
<!-- This SHOULD be the Agent object I clicked on to trigger the Command. This does NOT work, results in either exception or 'UnsetValue' -->
<Binding Source="{Binding}" />
<!-- Also pass in the Ticket item that was clicked on to open the context menu, this works fine -->
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListView}}" Path="SelectedItem" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
So here is what I am attempting to do;
The context menu has a single item "Send ticket as notification" which, when selected, lists all the available Agents which can receive said notification. This works.
When I click on one of these agent options, I want the context menu item to send both the ticket item that was clicked on from the listview to show the context menu (this works) AND the Agent item I selected from the context menu. I have half-achieved this with the MultiBinding
<MultiBinding Converter="{StaticResource ResourceKey=TicketNotificationParameterConverter}">
<MultiBinding.Bindings>
<!-- This works, it sends the Ticket object to the Converter -->
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListView}}" Path="SelectedItem" />
<!-- This caused an exception saying that I can only set 'Path' property on DependencyProperty types -->
<Binding Path="{Binding}" />
</MultiBinding.Bindings>
</MultiBinding>
Now although the actual set-up of the context menu seems some what complicated to me. The actual specification that one of the MultiBinding parameters should be the actual item bound to the clicked ContextMenu.MenuItem seems quite a simple command. The context menu correctly lists all Agents and so I would of thought that simply asking for this current object to be sent through as a command parameter, simple. I have also tried;
<Binding RelativeSource={RelativeSource AncestorType={x:Type MenuItem}} Path="SelectedItem" />
as well as
<Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}} Path="SelectedItem" />
But all that gets sent to the Parameter converter is UnsetValue
Thank you for your time and any suggestions you might have.
This line is odd:
<Binding Path="{Binding}" />
Is the current DataContext a string that would suffice as a path that would find something in the current DataContext which is in fact not a string after all? Makes perfect sense.
Did you mean to do this instead which binds to the DataContext?
<Binding />

Resources