【问题标题】:Binding the Loaded event?绑定 Loaded 事件?
【发布时间】:2011-12-14 19:55:30
【问题描述】:

我试图在我的 MainWindow 加载后显示一个登录窗口,同时坚持 MVVM 模式。所以我试图将我的主窗口加载事件绑定到我的视图模型中的一个事件。 这是我尝试过的:

MainWindowView.xaml

 <Window x:Class="ScrumManagementClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        DataContext="ViewModel.MainWindowViewModel"
        Loaded="{Binding ShowLogInWindow}">
    <Grid>

    </Grid>
 </Window>

MainWindowViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ScrumManagementClient.ViewModel
{
    class MainWindowViewModel : ViewModelBase
    {
        public void ShowLogInWindow(object sender, EventArgs e)
        {
            int i = 0;
        }
    }
}

我收到的错误消息是“Loaded="{Binding ShowLogInWindow}" 无效。'{Binding ShowLogInWindow}' 不是有效的事件处理程序方法名称。只有生成的或代码隐藏类上的实例方法是有效。”

【问题讨论】:

  • 哇 - 将近 10 年过去了,这仍然是个问题。

标签: c# wpf data-binding mvvm loaded


【解决方案1】:

您将不得不使用 System.Windows.Interactivity dll。

然后在你的 XAML 中添加命名空间:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

然后您可以执行以下操作:

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

请注意,您必须使用 ICommand(或者 DelegateCommand,如果您使用 Prism,或者 RelayCommand,如果您使用 MVVMLight),并且您的 Window 的 DataContext 必须包含该 ICommand。

【讨论】:

  • 小心。 System.Windows.Interactivity 不是 .NET 发行版的一部分。
  • 你可以在网上合法找到它,我相信它是 Blend 包的一部分
  • 感谢您的帮助,我开始有所收获,excetution 正在影响我的 ICommand 但没有进入 get{} 或 set{} 部分,知道如何解决这个问题吗?
  • 可以使用 System.Windows.Interactivity NuGet 包。
  • @LouisKottmann:这仍然是最先进的吗?想知道 .NET 中是否有新内容
【解决方案2】:

使用附加行为。这在 MVVM 中是允许的 ....

(下面的代码可能/可能不会像那样编译)

XAML ...

   <Window x:Class="..."
           ...
           xmlns:local="... namespace of the attached behavior class ..."
           local:MyAttachedBehaviors.LoadedCommand="{Binding ShowLogInWindowCommand}">
     <Grid>
     </Grid>
  </Window> 

代码背后...

  class MainWindowViewModel : ViewModelBase
  {
      private ICommand _showLogInWindowCommand;

      public ICommand ShowLogInWindowCommand
      {
         get
         {
              if (_showLogInWindowCommand == null)
              {
                  _showLogInWindowCommand = new DelegateCommand(OnLoaded)
              }
              return _showLogInWindowCommand;
         }
      }

      private void OnLoaded()
      {
          //// Put all your code here....
      }
  } 

以及附加的行为...

  public static class MyAttachedBehaviors
  {
      public static DependencyProperty LoadedCommandProperty
        = DependencyProperty.RegisterAttached(
             "LoadedCommand",
             typeof(ICommand),
             typeof(MyAttachedBehaviors),
             new PropertyMetadata(null, OnLoadedCommandChanged));

      private static void OnLoadedCommandChanged
           (DependencyObject depObj, DependencyPropertyChangedEventArgs e)
      {
          var frameworkElement = depObj as FrameworkElement;
          if (frameworkElement != null && e.NewValue is ICommand)
          {
               frameworkElement.Loaded 
                 += (o, args) =>
                    {
                        (e.NewValue as ICommand).Execute(null);
                    };
          }
      }

      public static ICommand GetLoadedCommand(DependencyObject depObj)
      {
         return (ICommand)depObj.GetValue(LoadedCommandProperty);
      }

      public static void SetLoadedCommand(
          DependencyObject depObj,
          ICommand  value)
      {
         depObj.SetValue(LoadedCommandProperty, value);
      }
  }

DelegateCommand 源代码可以在互联网上找到...它是最适合 MVVM 的 ICommand API。

edit:19.07.2016 修复了两个小语法错误

【讨论】:

  • 我不认为制作附加行为是经典的方法,交互触发器被广泛使用。此外,他将不得不处理 CommandParameter、CanExecute 等......不要重新发明轮子!
  • 我不认为互动是classic!由于各种点击和它带来的重新部署问题,我不喜欢交互性。我什至观察到它对于深度控制模板有点不可靠。另外,我发现自定义附加行为中的功能控制和所有权非常令人欣慰。
【解决方案3】:

AttachedCommandBehavior V2 aka ACB 提出了一种更通用的使用行为方式,它甚至支持多个事件到命令的绑定,

这是一个非常基本的使用示例:

<Window x:Class="Example.YourWindow"
        xmlns:local="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
        local:CommandBehavior.Event="Loaded"
        local:CommandBehavior.Command="{Binding DoSomethingWhenWindowIsLoaded}"
        local:CommandBehavior.CommandParameter="Some information"
/>

【讨论】:

  • 这在短期内有效,但是当我们想要挂钩第二个事件时呢,例如Unloaded 命令?
  • @ANeves 然后我认为移动到交互命名空间是要走的路: 等更多信息:stackoverflow.com/questions/1048517/…
  • 是的,感谢您的帮助。我想说的是,这种方法不是很有用——为了简单起见,我可以使用local:MyAttachedBehaviors.LoadedCommand="{Binding ShowLogInWindowCommand}",或者为了复杂性而使用交互 dll。这种中间解决方案很有趣,但我认为它不会很有用。
【解决方案4】:

对于 VS 2013 Update 5,我无法解决“无法将 'System.Reflection.RuntimeEventInfo' 类型的对象转换为类型 'System.Reflection.MethodInfo”。相反,在“核心”目录中,我制作了一个简单的界面

interface ILoad
    {
        void load();
    }

我的 viewModel 已经有了实现 ILoad 的 load() 函数。在我的 .xaml.cs 中,我通过 ILoad 调用 ViewModel load()。

private void ml_Loaded(object sender, RoutedEventArgs e)
{
    (this.ml.DataContext as Core.ILoad).load();
}

除了 POCO ILoad,xaml.cs 对 ViewModel 一无所知,ViewModel 对 xaml.cs 一无所知。 ml_loaded 事件映射到 ViewModel load()。

【讨论】:

    【解决方案5】:

    更新:

    我在这里发表了一篇关于方法绑定的更灵活的新版本的帖子,该版本使用了稍微不同的语法:

    http://www.singulink.com/CodeIndex/post/updated-ultimate-wpf-event-method-binding

    完整的代码列表在这里:

    https://gist.github.com/mikernet/7eb18408ffbcc149f1d9b89d9483fc19

    任何未来的更新都会发布到博客上,所以我建议在那里查看最新版本。

    原答案:

    .NET 4.5+ 现在支持事件的标记扩展。我用它来创建一个可以像这样使用的方法绑定:

    <!--  Basic usage  -->
    <Button Click="{data:MethodBinding OpenFromFile}" Content="Open" />
    
    <!--  Pass in a binding as a method argument  -->
    <Button Click="{data:MethodBinding Save, {Binding CurrentItem}}" Content="Save" />
    
    <!--  Another example of a binding, but this time to a property on another element  -->
    <ComboBox x:Name="ExistingItems" ItemsSource="{Binding ExistingItems}" />
    <Button Click="{data:MethodBinding Edit, {Binding SelectedItem, ElementName=ExistingItems}}" />
    
    <!--  Pass in a hard-coded method argument, XAML string automatically converted to the proper type  -->
    <ToggleButton Checked="{data:MethodBinding SetWebServiceState, True}"
                    Content="Web Service"
                    Unchecked="{data:MethodBinding SetWebServiceState, False}" />
    
    <!--  Pass in sender, and match method signature automatically -->
    <Canvas PreviewMouseDown="{data:MethodBinding SetCurrentElement, {data:EventSender}, ThrowOnMethodMissing=False}">
        <controls:DesignerElementTypeA />
        <controls:DesignerElementTypeB />
        <controls:DesignerElementTypeC />
    </Canvas>
    
        <!--  Pass in EventArgs  -->
    <Canvas MouseDown="{data:MethodBinding StartDrawing, {data:EventArgs}}"
            MouseMove="{data:MethodBinding AddDrawingPoint, {data:EventArgs}}"
            MouseUp="{data:MethodBinding EndDrawing, {data:EventArgs}}" />
    
    <!-- Support binding to methods further in a property path -->
    <Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" />
    

    查看模型方法签名:

    public void OpenFromFile();
    public void Save(DocumentModel model);
    public void Edit(DocumentModel model);
    
    public void SetWebServiceState(bool state);
    
    public void SetCurrentElement(DesignerElementTypeA element);
    public void SetCurrentElement(DesignerElementTypeB element);
    public void SetCurrentElement(DesignerElementTypeC element);
    
    public void StartDrawing(MouseEventArgs e);
    public void AddDrawingPoint(MouseEventArgs e);
    public void EndDrawing(MouseEventArgs e);
    
    public class Document
    {
        // Fetches the document service for handling this document
        public DocumentService DocumentService { get; }
    }
    
    public class DocumentService
    {
        public void Save(Document document);
    }
    

    更多详情可以在这里找到:http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension

    完整的课程代码可在此处获得: https://gist.github.com/mikernet/4336eaa8ad71cb0f2e35d65ac8e8e161

    【讨论】:

    • 这段代码非常有用,应该点赞。我遇到了一些问题并调整了代码以使其在我的解决方案中工作gist.github.com/alexis-/1b1d8b9791cba7c6fae5905c81d22766
    • 我将在几个小时内发布一些更新代码的更新,我只是没有时间。这是一个快速的概念证明,尚未经过全面测试。我们现在使用的版本有点不同——第一个参数是目标对象(可以接受完全绑定),第二个参数是目标上的方法名称,其余的都是要传入的参数。这使您可以更灵活地定位不同的绑定。
    • 您介意告诉我您遇到了什么问题,以便我确保新版本没有问题吗?
    • 当然,我已经评论了你的要点。快速思考:这实际上可以变成某种多目标动态绑定器 - 即根据 TargetProperty 类型调整提供的值:EventInfo、MethodInfo、...
    • 我将在我的博客上发布一篇简短的更新文章以及我们现在使用的更新代码,然后更新此答案。我们去掉了所有的 throw 选项并用 debug.write 替换它,因为绑定/转换器/等永远不应该抛出。还修复了在模板中使用时会崩溃的错误。现在正在努力:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多