Search code examples
wpfdatagrid

Why my multivalue converter is not fired?


I am trying to resize the column width of a datagrid with a converter. For testing, I have a tets application that has a checkbox to tell if use the value of the converter or not. And a textbox to tell which is the width to apply.

When I modify the checkbox or the textbox, the properties of the view model are notified, but the converter is not fired.

My code is this:

Main windows:

<Window x:Class="ModificarColumnaDataGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ModificarColumnaDataGrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    
    <Grid>
        <local:ucControlUsuario HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"/>
    </Grid>
</Window>

User control:

<UserControl x:Class="ModificarColumnaDataGrid.ucControlUsuario"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ModificarColumnaDataGrid"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <UserControl.Resources>
        <ResourceDictionary>
            <!--Este diccionario, el GUI, tendrá la configuración general para los diferentes elementos que se tengan en la GUI.-->
            <ResourceDictionary.MergedDictionaries>
                <!--Ruta relativa, porque esta vista está en subdirectorio, al mismo nivel que recursos.-->
                <ResourceDictionary Source="DiccionarioRecursos.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>


    <Grid>
        <CheckBox Content="Use Converter" HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top"
                  IsChecked="{Binding UseConverterIsChecked}"/>
        <TextBox Text="{Binding WidthColumn2, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="106,48,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>

        <!--Necesario para poder acceder a las propiedades del datacontext, necesario para los datatriggers.-->
        <FrameworkElement x:Name="ProxyElement" Visibility="Collapsed"/>

        <DataGrid Margin="0,154,0,0" AutoGenerateColumns="false">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Columna1" Width="200"/>
                <DataGridTextColumn Header="Columna2" Width="200">
                    <DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">
                            <!--<Setter Property="Width" Value="20"/>-->
                            <Setter Property="Width">
                                <Setter.Value>
                                    <MultiBinding Converter="{StaticResource MiMultiValueConverter}" >
                                        <Binding Source="{x:Reference ProxyElement}" Path="DataContext.UseConverterIsChecked" />
                                        <Binding Source="{x:Reference ProxyElement}" Path="DataContext.WidthColumn2" />
                                    </MultiBinding>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </DataGridTextColumn.ElementStyle>
                </DataGridTextColumn>
                <DataGridTextColumn Header="Columna3" Width="200"/>
            </DataGrid.Columns>
        </DataGrid>
        <Label Content="Pixels column 2" HorizontalAlignment="Left" Margin="0,44,0,0" VerticalAlignment="Top"/>

    </Grid>
</UserControl>

Code behind of user control:

namespace ModificarColumnaDataGrid
{
    /// <summary>
    /// Lógica de interacción para ucControlUsuario.xaml
    /// </summary>
    public partial class ucControlUsuario : UserControl
    {
        public ucControlUsuario()
        {
            InitializeComponent();


            DataContext = new ucControlUsuarioViewModel();
        }
    }
}

View model of user control:

namespace ModificarColumnaDataGrid
{
    class ucControlUsuarioViewModel : BaseViewModel
    {
        private bool _useConverterIsChecked;

        public bool UseConverterIsChecked
        {
            get { return _useConverterIsChecked; }
            set
            {
                _useConverterIsChecked = value;
                base.RaisePropertyChangedEvent(nameof(UseConverterIsChecked));
            }
        }


        private double _widthColumn2;

        public double WidthColumn2
        {
            get { return _widthColumn2; }
            set
            {
                _widthColumn2 = value;
                base.RaisePropertyChangedEvent(nameof(WidthColumn2));
            }
        }

    }
}

View model base:

public abstract class BaseViewModel : INotifyPropertyChanging, INotifyPropertyChanged { #region INotifyPropertyChanging Members

public event PropertyChangingEventHandler PropertyChanging;

#endregion

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion

#region Administrative Properties

/// <summary>
/// Whether the view model should ignore property-change events.
/// </summary>
public virtual bool IgnorePropertyChangeEvents { get; set; }

#endregion

#region Public Methods
//@#ESTUDIAR: si esta implemantación es mejor.
//Este método tiene la ventaja de que en el set de la propiedad, con poner OnPropertyChanged() es suficiente, no hace falta
//pasar por parámetro ni el stirng con el nombre de la propiedad ni siquiera la propiedad, porque la coge por defecto.
//Pero se puede pasar la propiedad en caso de que quiera notificar fuera del set, pero no hace falta pasar el string, sino la
//propiedad, por lo que el mantenimiento es mucho mejor.
////////////public void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string paramStrNombrePropiedad = "")
////////////{
////////////    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(paramStrNombrePropiedad));
////////////}


/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
public virtual void RaisePropertyChangedEvent(string propertyName)
{
    // Exit if changes ignored
    if (IgnorePropertyChangeEvents) return;

    // Exit if no subscribers
    if (PropertyChanged == null) return;

    // Raise event
    var e = new PropertyChangedEventArgs(propertyName);
    PropertyChanged(this, e);
}

/// <summary>
/// Raises the PropertyChanging event.
/// </summary>
/// <param name="propertyName">The name of the changing property.</param>
public virtual void RaisePropertyChangingEvent(string propertyName)
{
    // Exit if changes ignored
    if (IgnorePropertyChangeEvents) return;

    // Exit if no subscribers
    if (PropertyChanging == null) return;

    // Raise event
    var e = new PropertyChangingEventArgs(propertyName);
    PropertyChanging(this, e);
}

#endregion

}

Dicccionario de recursos:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:conv="clr-namespace:ModificarColumnaDataGrid"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <conv:MiMultiValueConverter x:Key="MiMultiValueConverter" />
</ResourceDictionary>

Multivalue converter:

namespace ModificarColumnaDataGrid
{
    public class MiMultiValueConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return 20;
        }


        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

How could I make the converter be fired?

EDIT:

I have tried to set the width of the column istead of the textblock of the style:

            <DataGridTextColumn.Width>
                <MultiBinding Converter="{StaticResource MiMultiValueConverter}" >
                                <Binding Source="{StaticResource BindingProxy}" Path="Data.UseConverterIsChecked"/>
                                <Binding Source="{StaticResource BindingProxy}" Path="Data.WidthColumn2" />
                            </MultiBinding>
            </DataGridTextColumn.Width>

But it doesn't work too.

However, if I use a converter directly in the Width property of the column it works:

<DataGridTextColumn Header="Columna2"
                                    Width="{Binding Data.UseConverterIsChecked, Converter={StaticResource MiValueConverter}, Source={StaticResource BindingProxy}}">

The problem with thi solution is that I need a multivalue converter, and I don't know how to use it in the width property of the column.

Thanks.


Solution

  • The problem with thi solution is that I need a multivalue converter, and I don't know how to use it in the width property of the column.

    Just set the Width property using object element syntax:

    <DataGridTextColumn Header="Columna2">
        <DataGridTextColumn.Width>
            <MultiBinding Converter="{StaticResource MiValueConverter}">
                <Binding Path="Data.UseConverterIsChecked" Source="{StaticResource BindingProxy}" />
        <!-- add more bindings here -->
            </MultiBinding>
        </DataGridTextColumn.Width>
    </DataGridTextColumn>