View property - bind twice - c#

Is it possible to bind the same property of the control more than once?
To example:
<Popup IsOpen="{Binding Path=(local:ListViewBehavior.IsColumnHeaderClicked),
RelativeSource={RelativeSource FindAncestor, AncestorType=GridViewColumnHeader}}" ...
As you can see Popup.IsOpen is bound to attached property. I'd like to bind it to ViewModel IsPopupOpened, but have no idea how.
Trying #Arhiman answer without much success:
<Popup.IsOpen>
<MultiBinding Converter="{local:MultiBindingConverter}">
<Binding Path="(local:ListViewBehavior.IsColumnHeaderClicked)"
RelativeSource="{RelativeSource FindAncestor, AncestorType=GridViewColumnHeader}" />
<Binding Path="DataContext.IsPopupId"
RelativeSource="{RelativeSource FindAncestor, AncestorType=UserControl}" />
</MultiBinding>
</Popup.IsOpen>
Naive converter logic:
public class MultiBindingConverter : MarkupExtension, IMultiValueConverter
{
public MultiBindingConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider) => this;
object[] _old;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (_old == null)
_old = values.ToArray();
// check if one of values is changed - that change is a new value
for (int i = 0; i < values.Length; i++)
if (values[i] != _old[i])
{
_old = values.ToArray();
return values[i];
}
return values[0];
}
// replicate current value
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
Enumerable.Repeat(value, targetTypes.Length).ToArray();
}

You could simply use a MultiBinding with a converter to implement the logic you'd like.
<Popup.IsOpen>
<MultiBinding Converter="{StaticResource openLogicConverter}">
<Binding Path="MyAttachedProperty" ... />
<Binding Path="IsPopupOpened" />
</MultiBinding>
</Popup.IsOpen>
I'd usually put this logic in the ViewModel, but as it is an AttachedProperty, something directly in the View seems more appropriate to me.

Related

Multibinding with RelayCommand returns unset values

I have a command that binds to a menu item I have, I would like to pass more than one parameter. I have tried using a converter however it seems to return nothing.
My converter
public class AddConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return values.Clone();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
My View model containing my command
class myViewModel: ViewModelBase {
private RelayCommand testCommand;
public ICommand TestCommand {
get {
if (testCommand == null) {
testCommand = new RelayCommand((param) => test((object[])param));
}
return testCommand ;
}
}
//Only trying to print out one of the params as a test
public void test(object parameter) {
var values = (object[])parameter;
int num1 = Convert.ToInt32((string)values[0]);
MessageBox.Show(num1.ToString());
}
My binding on my menu item
//Using tags as a test
<ContextMenu>
<MenuItem Name="testing" Header="Move to Position 10" Command="{Binding TestCommand}" Tag="7">
<MenuItem.CommandParameter>
<MultiBinding Converter="{StaticResource AddConverter}">
<Binding ElementName="testing" Path="Tag"/>
<Binding ElementName="testing" Path="Tag"/>
</MultiBinding>
</MenuItem.CommandParameter>
</MenuItem>
</ContextMenu>
After debugging, when I open my window containing the menu item, the converter fires off, the values object is null at that point. Then, when I select my menu item and fire off the command, when I get to my execution, the parameter is null. I don't understand why my converter fires off before I even click the menu item, or why the values are null.
Try to replace the ElementName of the bindings with a RelativeSource. This works for me:
<MenuItem Name="testing" Header="Move to Position 10" Command="{Binding TestCommand}" Tag="7">
<MenuItem.CommandParameter>
<MultiBinding Converter="{StaticResource AddConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</MenuItem.CommandParameter>
</MenuItem>
Also note that you should bind to the TestCommand property and not to the testCommand field.

WPF Label content shows DependencyProperty.UnsetValue in design mode

I have an WPF label and I have bound some data into a string using StringFormat from xaml:
<Label Grid.Row="0" Grid.Column="1" Style="{StaticResource MyLblResource}">
<Label.Content>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat="{}({0}) {1}">
<Binding Path="MyDataModel.Id" />
<Binding Path="MyDataModel.Desc" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Label.Content>
</Label>
Above code works fine, no problems but in design time, in xaml view, in the TextBlock content it is shown:
{{DependecyProperty.UnsetValue}} {{DependencyProperty.UnsetValue}}
Why is this being shown instead of being shown as empty? Is there any way to show this as empty?
This should do the trick:
public class StringFormatConverter : MarkupExtension, IMultiValueConverter
{
public string StringFormat { get; set; } = #"({0}) {1}";
public string PlaceHolder { get; set; } = "Empty";
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format(StringFormat, GetValues(values));
}
private IEnumerable<string> GetValues(object[] values)
{
foreach (var value in values)
yield return value == DependencyProperty.UnsetValue || value == null ? PlaceHolder : value.ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new[] { Binding.DoNothing, Binding.DoNothing };
}
}
Use it like this:
<MultiBinding Converter="{converter:StringFormatConverter PlaceHolder=MyPlaceHolderText}">
<Binding Path="MyDataModel.Id" />
<Binding Path="MyDataModel.Desc" />
</MultiBinding>
Please be aware that you can only set static values in StringFormat and PlaceHolder - because they are no DependencyProperty.

How to get the buttons current enabled/disabled status in WPF multibind 'Convert' method

My program has this kind of a multi bind
<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
<Binding Path="SelectedItems.Count"/>
<Binding Path="EffectiveStyleContext.Selection"/>
</MultiBinding>
IS there anyway to get the current enable disable status in the Convert method
class myConverter: IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//I need to get current status here
}
}
You have to pass control itself into a ValueConverter.
Your modified Xaml will be
<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="SelectedItems.Count"/>
<Binding Path="EffectiveStyleContext.Selection"/>
</MultiBinding>
Now in your coverter code you will be able to access control.
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var control = values[0] as FrameworkElement;
var value1 = values[1] as int;
// write your logic here.
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return throw new System.NotImplementedException();;
}
}
You can also bind direct to the property itself:
<Binding Path="IsEnabled" RelativeSource="{RelativeSource Self}" />
I would actually argue that this is preferable because the MultiBinding won't get updated when the enable state changes otherwise.

Is possible to get a index from a item in a list?

I mean, I've got a listBox, and I'm putting in itemsSource property the list. And I want to show also the index in the binding of it.
I have no idea if this is possible in WPF. Thanks.
There are a few methods for doing this including some workarounds using the AlternationIndex.
However, since I've used the AlternationIndex for other purposes I like to get a binding for the element index with the following:
<MultiBinding Converter="{StaticResource indexOfConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}" />
<Binding Path="."/>
</MultiBinding>
Where the converter is defined as:
public class IndexOfConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (Designer.IsInDesignMode) return false;
var itemsControl = values[0] as ItemsControl;
var item = values[1];
var itemContainer = itemsControl.ItemContainerGenerator.ContainerFromItem(item);
// It may not yet be in the collection...
if (itemContainer == null)
{
return Binding.DoNothing;
}
var itemIndex = itemsControl.ItemContainerGenerator.IndexFromContainer(itemContainer);
return itemIndex;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return targetTypes.Select(t => Binding.DoNothing).ToArray();
}
}

DependencyProperty.UnsetValue appears when using a IMultiValueConverter

I created a simple Converter to concatenate the text of four TextBoxes in my WPF app.
Here is the Converter:
public class FourString:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format("{0}{1}{2}{3}", values[0], values[1], values[2], values[3]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[] { };
}
}
in Xaml I use this code:
<local:FourString x:Key="converter"/>
<TextBox Grid.ColumnSpan="4" Margin="95,7.5,71.25,3.75" Name="CodeBoatTxt" >
<TextBox.Text>
<MultiBinding Converter="{StaticResource converter}" >
<Binding ElementName="CountryStringaTxt" Path="Text" />
<Binding ElementName="CityStringaTxt" Path="Text" />
<Binding ElementName="ServiceStringaTxt" Path="Text" />
<Binding ElementName="DurationStringaTxt" Path="Text" />
</MultiBinding>
</TextBox.Text>
</TextBox>
When in debug, this error appears in the CodeBoatTxt textbox: "DependecyProperty.UnsetValue".
What is wrong with my converter?
DependencyProperty.UnsetValue is passed into the converter when a Binding is valid, but does not have its value set yet. I would check the Bindings comprising your MultiBinding in isolation and ensure they are correct.

Categories

Resources