How to make this ItemsControl into StaticResource - wpf

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"/>

Related

Wpf Multibinding in a template or style

I have a class with many properties (not in a list) which must be switchable in view. The converter itself works fine using multibinding.
<TextBox Grid.Row="1" Grid.Column="5">
<TextBox.Text>
<MultiBinding Converter="{StaticResource IntValueConvertor}">
<Binding Path="property1" />
<Binding Path="IntegerDisplay" />
</MultiBinding>
</TextBox.Text>
</TextBox>
In the code, "IntegerDisplay" is a property which is defined in my VM.
Property1 is one of the many properties which must be viewed differently (depending on IntegerDisplay).
What I want to avoid is the need of adding the whole multibinding convertor to each textbox.
Something in this style:
<TextBox
Grid.Row="1"
Grid.Column="4"
Text="{Binding Path=Property1, Converter={StaticResource IntValueConvertor}}" />
I know this code does not work!
I tried using a style, but I could not get to the value of property1.
Is it the best way to use a style or is a datatemplate better?
Kind regards
I recommend sticking with a style and here's a basic start to doing so.
<!--This will go in your resources-->
<Style x:Name="TextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding Path=Property1, Converter={StaticResource IntValueConverter}, ConverterParameter=IntegerDisplay}"/>
</Style>
<!--This will go in your Display-->
<TextBox Style="{StaticResource TextBoxStyle}"/>

How to set foreground on part of multi binding when concatenating two properties in WPF XAML?

I am displaying two property values using multi-binding in WPF XAML.
I would want to set first/second value by different color.
<DataGridTemplateColumn Header="ConcatCol"
Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Right">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<MultiBinding StringFormat="{} {0:C1} / {1:C1}">
<Binding Path="FirstProp" />
<Binding Path="SecondProp" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
So basically you can see that I am displaying two property values in a single cell but separating them by "/". I am also specifying the string format for each of the property.
Now my question is How can I display the value before "/" in Green color and the latter in Red color?
You could actually use Textblock with multiple runs:
<TextBlock TextAlignment="Right">
<Run Text="{Binding FirstProp}" Foreground="Green"/> / <Run Text="{Binding SecondProp}" Foreground="Red"/>
</TextBlock>

Why is the binding failing within a ItemsControl

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>

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

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