Passing Guid to IMultiValueConverter - c#

I've created a multivalueconverter that I'm binding a telerik:RadMenuItem's visibility property. This right-click menu is inside of an appointment that goes onto a Telerik calendar control. I want to pass the ControlID (which is an attached property from the framework I'm using) into the IMultiValueConverter that I've made as a parameter. So, I want to pass in the Guid (which comes in as the second value in an object array in the converter) and then cast it something to get the value so I can do my evaluations. WPF isn't my strong suit and after trying a lot of things I can't seem to get the binding wired up.
<telerik:RadMenuItem Header="Cancel" x:Name="CancelMenuItem"
Click="RadMenuItemCancel_Click"
myframework:BaseWindow.ControlID="e5c25731-e30e-472e-a5d7-ab190348a7cb">
<telerik:RadMenuItem.Visibility>
<MultiBinding Converter="{StaticResource SecurityEnumToVisibilityConverter}">
<Binding Path="Appointment.AppointmentType" />
<Binding ElementName="CancelMenuItem" Path="myframework:BaseWindow.ControlID" />
</MultiBinding>
</telerik:RadMenuItem.Visibility>
</telerik:RadMenuItem>
What am I missing here in that second Binding tag to pass in the contents of ControlID to my multivalue converter successfully? Thanks!

Try surrounding your path with brackets.
Path="(myframework:BaseWindow.ControlID)"
afaik this tells WPF that you are binding to an attached property

Related

DataContext accessible from IMultiValueConverter

I have WPF application and I need to bind two arguments (no matter what they are) to calculate the value (checkbox checked or not). So I have to use IMultiValueConverter and that's fine.
But is there a way, to give this converter access to DataContext (ViewModel) of a window I am binding to?
Basically I have some checkboxes in treeview, i need to pass to converter content (text) of theese checkboxes and its parent's header. Then in converter I need to process that text and find out if it's present in some collection I have in my ViewModel (DataContext). I know that I cannot use ConverterParameter, because it doesn't support binding.
Just add another Binding to your MultiBinding that binds to the view model, e.g.:
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Property1" />
<Binding Path="Property2" />
<Binding Path="DataContext" RelativeSource="{RelativeSource AncestorType=Window}" />
</MultiBinding>

Storing ValueConverter to variable

I am having a ValueConverter used for binding 'To' Value in a StoryBoard animation, similar to the answer - WPF animation: binding to the “To” attribute of storyboard animation.
The problem is I am repeating the below piece of code for MultiBinding ValueConverter in couple of places.
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
I want to remove this duplicate code by storing the result of the ValueConverter to a resource variable so I can bind this local Variable directly to the story board.
<system:Double x:Key="CalculatedWidth">
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</system:Double >
I am getting the following error:
The type 'Double' does not support direct content.
Cannot add content to an object of type "Double".
I feel this is a common problem but not able to find a solution to remove this redundancy.
Update
Thanks Rohit, your answer solved the problem. But I have one more related issue, So updating the question. This variable CalculatedWidth works fine in normal case, but when it is used in RenderTransform it doesn't pick up the value. i.e. If I use the normal way to use Converter it works but it doesn't pick up the variable.
<StackPanel.RenderTransform>
<TranslateTransform x:Name="SliderTransform">
<TranslateTransform.X>
<Binding Converter="{StaticResource PanelConverter}" ElementName="SliderPanel" Path="ActualWidth" /> // Works
<Binding Path="Width" Source="{StaticResource CalculatedWidth}"/> // Doesn't Work
</TranslateTransform.X>
</TranslateTransform>
</StackPanel.RenderTransform>
I have kept the variable as part of the local resource. Does this mean the variable doesn't get created when Render transform is called?
As the error suggest you can't bind with Double. Binding can be done with only Dependency properties.
Instead use FrameworkElement in resource and bind its Width(DP) like this:
<FrameworkElement x:Key="CalculatedWidth">
<FrameworkElement.Width>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</FrameworkElement.Width>
</FrameworkElement>
and you can bind with this resource like in this sample:
<TextBlock Width="{Binding Width, Source={StaticResource CalculatedWidth}}"/>
A System.Double doesn't implements INotifyPropertyChange (and doesn't show a Value property to notify on) nor it implements dynamic properties advanced binding mechanisms. So it cannot notify of its changes.
The problem with local resources is their instanciation : they do not have visibility to hosting namescope because they are instanciated outside it. So doesn't bind to nothing and the binding returns DependancyProperty.UnsetValue.
The is relative to the FrameworkElement resource itself and returns its Tag property value: null.
If you use VS2013 with .NET 4.5 (maybe it works also with VS2012/.NET 4.0), look at Output window for data binding trace :
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'ElementName=ExpanderContent'. BindingExpression:Path=ActualHeight; DataItem=null; target element is 'FrameworkElement' (Name=''); target property is 'Width' (type 'Double')
Distinct solutions are offered to you : you can move the FrameworkElement outside the local resources (remember that you probably have to add HorizontalAlign="Left" to allow Width changes. Another solution is to add a dependancy property to the code behind.Finally, you want to share the result of your multiplier converter between more than one control (or properties): The simplest way is maybe to bind it the first property of the first control and to bind other controls properties to this first property.

Use another element's ItemsSource property in a MultiBinding

I'd like to use the ItemsSource property from a particular element as one of the bindings in another element's MultiBinding. Here's what I have so far:
<Label>
<Label.Content>
<MultiBinding Converter="{converters:myMultiValueConverter}">
<Binding Path="PageIndex" />
<Binding ElementName="anotherElement" Path="ItemsSource"/>
</MultiBinding>
</Label.Content>
</Label>
This works once (when the ItemsSource is initially set), but the binding fails to update when the ObservableCollection bound to the original element's ItemsSource property has items added or removed. Is this kind of binding possible?
Add a dummy binding (- you don't need the value -) like this to force the MultiBinding to be reevaluated:
<Binding ElementName="anotherElement" Path="ItemsSource.Count"/>
Edit: Just noticed a flaw: If you move items that would not register if that does not change the Count property in-between, maybe this is relevant for you. In that case you could bind to your own dummy for which you can fire change notifications upon CollectionChanged (not all that clean in any case though).
You might want to consider HighCore's suggestion, a get-only property that returns the calculated value for which you manually fire PropertyChanged in all places that it depends on is usually quite convenient.

Binding and support for rich text formatting

Consider the following scenario:
I have a ListView that is bound to an ObservableCollection using the DataContext:
<ListView ItemsSource="{Binding}">
The class containing the string data uses the DependencyProperty mechanism to keep the displayed content synced with the data collection.
The ListView has one column that is editable (I followed the tutorial here to achieve this); the ListViewItem is then either a TextBlock or a TextBox. This is done using a DataTemplate and two Style resources.
I'd like to format the string displayed in the TextBlock based on a search string. Specifically, I'd like to format the items of the ListView to become bold as the user types in their search query if there is a match (only the characters that match in sequence should be made bold). This only needs to be displayed for the text currently being rendered using the TextBlock (that is, text not currently being edited).
I've considered using an IMultiValueConverter that takes in a reference to the TextBlock that renders the data so that I can format the text appropriately. However, this will destroy the binding that I've set up:
<TextBlock.Text>
<MultiBinding Converter="{StaticResource searchFormatter}" ConverterParameter="{x:Reference Name=txtSearch}">
<MultiBinding.Bindings>
<Binding Path="NameOfBoundDependencyProperty"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
searchFormatter being the IMultiValueConverter and txtSearch being the TextBox containing the search query.
I'm still learning WPF so I'm not familiar with the best approach or to what's possible. Is there a way to keep the data bound (so that edits reflect in the collection and the ListView) and still represent the data differently to the user (so that search matches may be bold)? Perhaps it would be cleaner if I manage the binding manually?
I decided to use a Control that supports HTML so that I could use an IValueConverter to update the value of the displayed text on the fly without affecting any active bindings. I used the code from here and modified it so that it looked like a TextBlock within my ListView:
BorderBrush = Brushes.Transparent;
SelectionBrush = Brushes.Transparent;
Cursor = Cursors.Arrow;
BorderThickness = new Thickness(0);
Background = Brushes.Transparent;
However, I still needed to trigger the IValueConverter so that the display is updated as the user types in their search query (code from here):
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
view.Refresh();
I didn't want to slow down the search process so I only forced this refresh if there was actually a match (or if the state of having a match moves to no match). My IValueConvertor simply inserted the bold tags to match the search query:
<RichTextBox Text="{Binding Path=DisplayItem, Converter={StaticResource searchFormatter}, ConverterParameter={x:Reference txtSearch}}"/>
Where searchFormatter this time is an IValueConvertor.

How can I get my validation code to execute on an item with no binding?

The Problem:
I am creating user control that handles data conversions (via a converter/validation rule). This works 100% as desired, but the validation only fires when the control is bound to something, which is not always the case.
Is there a way to force validation even if the control is not bound? OR is there a way to set up basically a dummy binding. (The solution needs to be done in code so that the end result is a drag and drop user control with no xaml customization needed by the programmer.
Thanks in advance for any advice.
EDIT : Really the code in question is this:
Binding TextBinding = BindingOperations.GetBinding(this, TextBox.TextProperty);
TextBinding.ValidationRules.Add(MyValidationRule);
This is how I am assigning my validation rule, but it will only work if the TextBinding is not null. So I either need a dummy binding for my TextBox, or another way to add the validation rule.
Sounds to me like what you want to be doing is defining a dependency property on your user control which the XAML in your user control binds to. This binding would incorporate your validation rule. Consumers of your user control would then bind the property on your user control to whatever they want.
It's really hard to be more specific than that without your exact use case, but consider this:
CoolUserControl.xaml.cs:
public class CoolUserControl : UserControl
{
public static readonly DependencyProperty CoolProperty = ...;
public string Cool
{
// get / set
}
}
CoolUserControl.xaml:
<UserControl x:Name="root" ...>
<TextBox>
<TextBox.Text>
<Binding Path="Cool" ElementName="root">
<Binding.ValidationRules>
<!-- your rule here -->
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</UserControl>
SomeConsumer.xaml:
<local:CoolUserControl Cool="{Binding SomePropertyOnMyViewModel}"/>

Categories