Привязать событие Loaded?

Я пытаюсь отобразить окно входа в систему после загрузки MainWindow, придерживаясь шаблона MVVM. Итак, я пытаюсь привязать событие Loaded в главном окне к событию в моей модели просмотра. Вот что я пробовал:

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}' не является допустимым именем метода обработчика событий. Допустимы только методы экземпляра в сгенерированном классе или классе программной части."


person Eamonn McEvoy    schedule 25.10.2011    source источник
comment
вау - прошло почти 10 лет, и это все еще проблема.   -  person Paul McCarthy    schedule 16.07.2021


Ответы (5)


Вам нужно будет использовать dll System.Windows.Interactivity.

Затем добавьте пространство имен в свой 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), и DataContext вашего окна должен содержать эту ICommand.

person Louis Kottmann    schedule 25.10.2011
comment
Осторожный. System.Windows.Interactivity не является частью дистрибутива .NET. - person Kugel; 25.10.2011
comment
Вы можете найти его легально в Интернете, я считаю, что это часть пакета Blend - person Louis Kottmann; 25.10.2011
comment
спасибо за помощь, я начинаю куда-то добраться, исключение попадает в мою команду ICommand, но не входит в разделы get {} или set {}, есть идеи, как это исправить? - person Eamonn McEvoy; 25.10.2011
comment
На самом деле ICommand указывает на метод. См. Этот вопрос в SO: stackoverflow.com/questions/1468791/ - person Louis Kottmann; 25.10.2011
comment
Вы можете использовать пакет NuGet System.Windows.Interactivity. - person honzakuzel1989; 19.07.2016
comment
@LouisKottmann: это все еще современное состояние? интересно, есть ли что-то новое в .NET - person Kaspatoo; 06.03.2020

Используйте прикрепленное поведение. Это разрешено в 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 исходный код можно найти в Интернете ... Это наиболее подходящий API ICommand, доступный для MVVM.

edit: 19.07.2016 исправлены две незначительные синтаксические ошибки

person WPF-it    schedule 25.10.2011
comment
Я не верю, что создание привязанного поведения - это классический способ, триггеры взаимодействия широко используются. Кроме того, ему придется обрабатывать CommandParameter, CanExecute и т. Д. ... не изобретайте велосипед! - person Louis Kottmann; 25.10.2011
comment
Я не думаю, что интерактивность classic! Я не фанат интерактивности из-за различных проблем, связанных с однократным щелчком и повторным развертыванием. Я даже заметил, что это немного ненадежно для шаблонов глубокого контроля. Кроме того, я нахожу контроль и владение функциональностью очень удобными в настраиваемом прикрепленном поведении. - person WPF-it; 25.10.2011

Обновление:

Я сделал сообщение о новой, более гибкой версии привязки метода, которая здесь использует немного другой синтаксис:

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/4336eaa8ad71cb160f8e35e

person Mike Marynowski    schedule 05.07.2016
comment
Я опубликую обновление с новым кодом через несколько часов, у меня просто не было времени. Это было быстрое подтверждение концепции и еще не было полностью протестировано. Версия, которую мы сейчас используем, работает немного по-другому: первый параметр - это целевой объект (который может принимать полную привязку), второй параметр - это имя метода в целевом объекте, а остальные - это параметры, которые нужно передать. Это дает вам больше гибкости для настройки различных привязок. - person Mike Marynowski; 23.09.2016
comment
Не могли бы вы сказать мне, какая у вас проблема, чтобы я мог убедиться, что в новой версии ее нет? - person Mike Marynowski; 23.09.2016
comment
Конечно, я прокомментировал вашу суть. Быстрая мысль: это на самом деле можно превратить в своего рода многоцелевое динамическое связывание, то есть настроить предоставленное значение в зависимости от типа TargetProperty: EventInfo, MethodInfo, ... - person Alexis; 24.09.2016
comment
Я опубликую короткую обновленную статью в своем блоге и обновленный код, который мы используем сейчас, а затем обновим этот ответ. Мы удалили все параметры throw и заменили его на debug.write, поскольку привязки / преобразователи / и т. Д. Никогда не должны сбрасывать. Также исправлена ​​ошибка, из-за которой он вылетал при использовании внутри шаблонов. Работаем над этим сейчас :) - person Mike Marynowski; 24.09.2016
comment
Будем разбираться в этом, спасибо за внимание. Пожалуйста, предоставьте лицензию (желательно не вирусную - в идеале она будет соответствовать требованиям MIT, если вам это удобно). Алекс. - person Alexis; 24.09.2016

Более общий способ использования поведений предлагается в 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"
/>
person JoanComasFdz    schedule 12.12.2012
comment
Это работает в краткосрочной перспективе, но как насчет того, чтобы перехватить второе событие, например команда Unloaded? - person ANeves thinks SE is evil; 29.02.2016
comment
@ANeves Тогда я думаю, что лучше всего перейти к пространствам имен Interaction: ‹i: Interaction.Triggers› ‹i: EventTrigger EventName = SomeEvent› ‹i: InvokeCommandAction Command = {Binding Path = SomeCommand, Mode = OneWay} /› и т. д. Дополнительная информация: stackoverflow.com/questions/1048517/ - person JoanComasFdz; 01.03.2016
comment
Да, спасибо за помощь. Я хотел сказать, что этот подход не очень полезен - я могу использовать local:MyAttachedBehaviors.LoadedCommand="{Binding ShowLogInWindowCommand}" для простоты или DLL взаимодействия для сложности. Это промежуточное решение интересно, но не думаю, что оно будет очень полезным. - person ANeves thinks SE is evil; 01.03.2016

Для VS 2013 Update 5 мне не удалось обойти «Невозможно привести объект типа 'System.Reflection.RuntimeEventInfo' к типу 'System.Reflection.MethodInfo». Вместо этого в каталоге "Core" я сделал простой интерфейс.

interface ILoad
    {
        void load();
    }

В моей модели viewModel уже была функция load (), реализующая ILoad. В моем .xaml.cs я вызываю ViewModel load () через ILoad.

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

Xaml.cs ничего не знает о ViewModel, кроме POCO ILoad, ViewModel ничего не знает о xaml.cs. Событие ml_loaded сопоставляется с ViewModel load ().

person user2584621    schedule 27.10.2015