MVVM - WPF DataGrid - AutoGenerateColumn 事件

发布于 2024-09-26 03:58:44 字数 2261 浏览 3 评论 0原文

我目前正在仔细研究 Laurent 的excellent工具包,我有以下问题。

从 Blend 4 开始,我为 Loaded 事件添加了一个 EventTrigger,在我的 ViewModel 中,我有以下内容: 在

public RelayCommand rcAutoGeneratingColumn { get; private set; }

构造函数中,我有:

rcAutoGeneratingColumn = 
   new RelayCommand(o => DataGridAutoGeneratingColumn(o));

同样在 ViewModel 中,我有我希望由 RelayCommand 调用的方法:

    private void DataGridAutoGeneratingColumn(Object o)
    {
        DataGrid grid = (DataGrid)o;

        foreach (DataGridTextColumn col in grid.Columns)
        {
            if (col.Header.ToString().ToLower() == "id")
            {
                col.Visibility = System.Windows.Visibility.Hidden;
            }
        }
    }

我的 XAML 包含以下内容(对于 DataGrid):

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <GalaSoft_MvvmLight_Command:EventToCommand 
            Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
            CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

这里没有问题 代码工作得很好,但显然用于隐藏某些列的事件应该是 AutoGenerateColumn 事件而不是 Loaded 。 我曾经使用 Loaded 事件作为解决方法。

我希望我可以中继控件提供的任何事件,以便在这种情况下,以下内容可以代替:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="AutoGeneratingColumn">
        <GalaSoft_MvvmLight_Command:EventToCommand 
            Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
            CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

我无法触发 AutoGenerateColumn 事件,并且我希望我忽略了一些事情并欣赏任何建议!

此行为与 DevExpress 中的 GridControl 相同,因为会触发 Loaded 事件,而不会触发 ColumnsPopulated 事件(这相当于 AutoGenerateColumn 事件)。

DevExpress 针对我的问题提供了以下信息:

我们已经审查了这个问题,并得出了一个有趣的结论。看起来在处理 Interaction.Triggers 时并未构建可视化树< /em>"

如果这是真的,并且没有其他方法可以调用 ViewModel 中的事件,那么就必须继续 - 通过使用试验和错误 - 记下哪些 DataGrid 事件(其中有超过 100)可以通过这种方式调用,但不能!

人们可能会认为,在应用 MVVM 模式时,也可以访问代码隐藏中可用的每个事件。

我已经寻找答案,但我不能排除我忽略了一些东西,所以如果是这种情况,请接受我的道歉!

I'm currently taking a good look at the excellent toolkit from Laurent and I have the following question.

From Blend 4, I have added an EventTrigger for the Loaded event, in my ViewModel I have the following:

public RelayCommand rcAutoGeneratingColumn { get; private set; }

In the constructor I have:

rcAutoGeneratingColumn = 
   new RelayCommand(o => DataGridAutoGeneratingColumn(o));

Also in the ViewModel, I have the method which I wish to be invoked by the RelayCommand:

    private void DataGridAutoGeneratingColumn(Object o)
    {
        DataGrid grid = (DataGrid)o;

        foreach (DataGridTextColumn col in grid.Columns)
        {
            if (col.Header.ToString().ToLower() == "id")
            {
                col.Visibility = System.Windows.Visibility.Hidden;
            }
        }
    }

My XAML contains the following (for the DataGrid):

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <GalaSoft_MvvmLight_Command:EventToCommand 
            Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
            CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

There is NO PROBLEM here the code works just fine, but obviously the event used to hide certain columns should be the AutoGeneratingColumn event and not Loaded.
I have used to Loaded event as a getaround.

I was hoping that I could relay any event offered by the control so that, in this case, the following would work instead:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="AutoGeneratingColumn">
        <GalaSoft_MvvmLight_Command:EventToCommand 
            Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
            CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

I am unable to get the AutoGeneratingColumn event to trigger, and I'm hoping that I've overlooked something and appreciate any advice given!

This behaviour is the same with the GridControl from DevExpress, in that the Loaded event is triggered whereas the ColumnsPopulated event (this being the equivalent of the AutoGeneratingColumn event) is not.

DevExpress offered the following information with regard to my question:

"We have reviewed this question, and come to an interesting conclusion. It looks like the visual tree is not being built at the moment when the Interaction.Triggers are being processed"

If this is true, and there is no other way in which to invoke the events within the ViewModel, then one would have to go ahead and - by using trial and error - note which of the DataGrid events (of which there are over 100) can be invoked in this way and which cannot!

One would like to think that every event which is available in the code-behind, can also be reached when applying the MVVM pattern.

I have searched for an answer but I cannot rule out that I have overlooked something, so if this is to be the case, then please accept my apologies!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

乄_柒ぐ汐 2024-10-03 03:58:44

您不必在后面使用邪恶的代码;-) 您可以使用附加行为来执行此操作...

public class AutoGeneratingColumnEventToCommandBehaviour
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            "Command", 
            typeof(ICommand), 
            typeof(AutoGeneratingColumnEventToCommandBehaviour),
            new PropertyMetadata(
                null,
                CommandPropertyChanged));

    public static void SetCommand(DependencyObject o, ICommand value)
    {
        o.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject o)
    {
        return o.GetValue(CommandProperty) as ICommand;
    }

    private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid != null)
        {
            if (e.OldValue != null)
            {
                dataGrid.AutoGeneratingColumn -= OnAutoGeneratingColumn;
            }
            if (e.NewValue != null)
            {
                dataGrid.AutoGeneratingColumn += OnAutoGeneratingColumn;
            }
        }
    }

    private static void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        var dependencyObject = sender as DependencyObject;
        if (dependencyObject != null)
        {
            var command = dependencyObject.GetValue(CommandProperty) as ICommand;
            if (command != null && command.CanExecute(e))
            {
                command.Execute(e);
            }
        }
    }
}

然后在 XAML 中使用它,如下所示...

<DataGrid ItemsSource="{Binding MyGridSource}"
          AttachedCommand:AutoGeneratingColumnEventToCommandBehaviour.Command="{Binding CreateColumnsCommand}">
</DataGrid>

You don't have to use evil code behind ;-) You can do this using an attached behaviour...

public class AutoGeneratingColumnEventToCommandBehaviour
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            "Command", 
            typeof(ICommand), 
            typeof(AutoGeneratingColumnEventToCommandBehaviour),
            new PropertyMetadata(
                null,
                CommandPropertyChanged));

    public static void SetCommand(DependencyObject o, ICommand value)
    {
        o.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject o)
    {
        return o.GetValue(CommandProperty) as ICommand;
    }

    private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        if (dataGrid != null)
        {
            if (e.OldValue != null)
            {
                dataGrid.AutoGeneratingColumn -= OnAutoGeneratingColumn;
            }
            if (e.NewValue != null)
            {
                dataGrid.AutoGeneratingColumn += OnAutoGeneratingColumn;
            }
        }
    }

    private static void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        var dependencyObject = sender as DependencyObject;
        if (dependencyObject != null)
        {
            var command = dependencyObject.GetValue(CommandProperty) as ICommand;
            if (command != null && command.CanExecute(e))
            {
                command.Execute(e);
            }
        }
    }
}

Then use it in XAML like this...

<DataGrid ItemsSource="{Binding MyGridSource}"
          AttachedCommand:AutoGeneratingColumnEventToCommandBehaviour.Command="{Binding CreateColumnsCommand}">
</DataGrid>
挽你眉间 2024-10-03 03:58:44

只需设置 EventTrigger.SourceObject 属性即可。

<DataGrid
  x:Name="DataGrid"
  AutoGenerateColumns="True"
  IsReadOnly="True"
  ItemsSource="{Binding Data}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
      <local:EventToCommand
        Command="{Binding ColumnGeneratingCommand}"
        PassEventArgsToCommand="True" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</DataGrid>

Just set EventTrigger.SourceObject property.

<DataGrid
  x:Name="DataGrid"
  AutoGenerateColumns="True"
  IsReadOnly="True"
  ItemsSource="{Binding Data}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
      <local:EventToCommand
        Command="{Binding ColumnGeneratingCommand}"
        PassEventArgsToCommand="True" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</DataGrid>
暮凉 2024-10-03 03:58:44

由于 Galasoft 的 MVVMLight 现在已被弃用,我们可以使用 CommunityToolkit.Mvvm 包并像这样使用它:

<DataGrid AutoGenerateColumns="True" 
          Name="DataGrid"
          ItemsSource="{Binding Items}">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
            <i:InvokeCommandAction Command="{Binding AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

请注意,Items 属性是一个简单的列表,它可以是 ObservableCollection 或其他任何东西。

获取触发事件的技巧是在加载窗口后加载数据,或者在加载后在 Items 属性上引发 OnpropertyChanged。

<Window ...>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Window>

在您的视图模型中:

private RelayCommand<DataGridAutoGeneratingColumnEventArgs> myAutoGeneratingColumnCommand;
public RelayCommand<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumnCommand
{
    get
    {
        if (myAutoGeneratingColumnCommand == null)
            myAutoGeneratingColumnCommand = new RelayCommand<DataGridAutoGeneratingColumnEventArgs>(AutoGeneratingColumnCommandAction);

        return myAutoGeneratingColumnCommand;
    }
}

private void AutoGeneratingColumnCommandAction(DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyName == "Id")
    {
        e.Column.Width = 60;
    }
    else if (e.PropertyName == "Name")
    {
        e.Column.Header = "myName";
        e.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
    }
    else
        e.Cancel = true; // ignore all other properties and remove their column     
}

RelayCommand myLoadedCommand;
public RelayCommand LoadedCommand
{
    get
    {
        if (myLoadedCommand == null)
            myLoadedCommand = new RelayCommand(LoadedCommandAction);

        return myLoadedCommand;
    }
}

private void LoadedCommandAction()
{   
    Load(); // Populate the Items List
}

As MVVMLight from Galasoft is deprecated now, we can use CommunityToolkit.Mvvm package and use it like this:

<DataGrid AutoGenerateColumns="True" 
          Name="DataGrid"
          ItemsSource="{Binding Items}">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
            <i:InvokeCommandAction Command="{Binding AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

Note that Items property is a simple List, It could be an ObservableCollection or whatever.

The trick to get the fired event is to load your data after the window is loaded, or raise OnpropertyChanged on Items property after loaded.

<Window ...>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Window>

In your View Model:

private RelayCommand<DataGridAutoGeneratingColumnEventArgs> myAutoGeneratingColumnCommand;
public RelayCommand<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumnCommand
{
    get
    {
        if (myAutoGeneratingColumnCommand == null)
            myAutoGeneratingColumnCommand = new RelayCommand<DataGridAutoGeneratingColumnEventArgs>(AutoGeneratingColumnCommandAction);

        return myAutoGeneratingColumnCommand;
    }
}

private void AutoGeneratingColumnCommandAction(DataGridAutoGeneratingColumnEventArgs e)
{
    if (e.PropertyName == "Id")
    {
        e.Column.Width = 60;
    }
    else if (e.PropertyName == "Name")
    {
        e.Column.Header = "myName";
        e.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
    }
    else
        e.Cancel = true; // ignore all other properties and remove their column     
}

RelayCommand myLoadedCommand;
public RelayCommand LoadedCommand
{
    get
    {
        if (myLoadedCommand == null)
            myLoadedCommand = new RelayCommand(LoadedCommandAction);

        return myLoadedCommand;
    }
}

private void LoadedCommandAction()
{   
    Load(); // Populate the Items List
}
绿萝 2024-10-03 03:58:44

在使用 MVVM 开发项目的过程中,您可能会遇到必须在视图的代码隐藏中处理事件的情况,而 EventToCommand 根本不起作用。您尤其会在 Silverlight 中发现这一点,但我从您的问题中假设您正在使用 WPF。在视图的代码隐藏中进行一些事件处理是可以的,只是不要在那里放置任何业务逻辑。您甚至可以将该命令保留在视图模型中,只需直接从事件处理程序调用它即可。

((YourViewModel)this.DataContext).rcAutoGeneratingColumn.Execute(sender);

During the course of developing a project with MVVM you're going to have circumstances where you must handle events in your view's code-behind and EventToCommand just plain doesn't work. You especially find this with Silverlight, but I assume from your question that you're using WPF. It's okay to do some event handling in your view's code-behind, just don't put any business logic there. You can even leave the command in your view model, just call it directly from your event handler.

((YourViewModel)this.DataContext).rcAutoGeneratingColumn.Execute(sender);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文