it-source

ViewModel의 명령에 WPF 바인딩 UI 이벤트

criticalcode 2023. 5. 16. 22:43
반응형

ViewModel의 명령에 WPF 바인딩 UI 이벤트

MVVM을 추적하기 위해 간단한 애플리케이션을 리팩터링하고 있는데, 어떻게 하면 SelectionChanged 이벤트를 코드에서 ViewModel 뒤로 이동할 수 있습니까?명령에 대한 바인딩 요소의 몇 가지 예를 살펴보았지만 제대로 파악하지 못했습니다.누가 이것을 도와줄 수 있습니까?감사합니다!

아래 코드를 사용하여 해결책을 제공할 수 있는 사람이 있습니까?감사합니다!

public partial class MyAppView : Window 
{
    public MyAppView()
    {
        InitializeComponent();

        this.DataContext = new MyAppViewModel ();

        // Insert code required on object creation below this point.
    }

    private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        //TODO: Add event handler implementation here.           
        //for each selected contact get the labels and put in collection 

        ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>();

        foreach (ContactListModel contactList in contactsList.SelectedItems)
        {
            foreach (AggregatedLabelModel aggLabel in contactList.AggLabels)
            {
                contactListLabels.Add(aggLabel);
            }
        }
        //aggregate the contactListLabels by name
        ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels);

        selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
        tagsList.ItemsSource = selectedLabelsView.Groups;
    }
}

다음을 사용해야 합니다.EventTrigger InvokeCommandAction.네임스페이스입니다.상호 작용 네임스페이스입니다.다음은 예입니다.

<ListBox ...>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

다니있습을 참조할 수 .System.Windows.Interactivity 길에Add reference > Assemblies > Extensions.

완전한 리고전체그.i는 다음과 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

이 질문에도 비슷한 문제가 있습니다.

WPF MVVM: 명령은 쉽습니다.라우팅된 이벤트로 뷰와 뷰 모델을 연결하는 방법

이 문제를 해결하는 방법은 선택 항목을 설정하는 것입니다.View Model의 항목 속성을 선택한 다음 바인딩ListBox 항목 또는 해당 속성에 대한 항목입니다.

이것을 재요인화하기 위해서는 당신의 생각을 바꿀 필요가 있습니다.더 이상 "선택 변경" 이벤트를 처리하지 않고 선택한 항목을 보기 모델에 저장합니다.그런 다음 양방향 데이터 바인딩을 사용하여 사용자가 항목을 선택하면 보기 모델이 업데이트되고 선택한 항목을 변경하면 보기가 업데이트됩니다.

Microsoft를 생각해 보십시오.자, 행동들.Wpf, 그것의 주인은.Microsoft당신이 그 페이지에서 볼 수 있는 것.

시스템. 윈도우.상호작용성.WPF 소유자는mthamil믿을 만한 사람이 있나요?

Microsoft.Xaml.Behaviors.Wpf:

<UserControl ...
             xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
             ...>

<Button x:Name="button">
    <behaviors:Interaction.Triggers>
        <behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
            <behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
        </behaviors:EventTrigger>
    </behaviors:Interaction.Triggers>
</Button>

</UserControl>

당신의 최선의 방법은 사용하는 것입니다.Windows.Interactivity.사용하다EventTriggersICommand어느 누구에게도RoutedEvent.

시작하기 위한 기사가 있습니다: SilverlightWPF 동작트리거

조금 늦었다는 것은 알지만, 마이크로소프트는 그들의 Xaml을 만들었습니다.동작은 오픈 소스이며 이제 하나의 네임스페이스만으로 상호 작용을 훨씬 쉽게 사용할 수 있습니다.

  1. Microsoft 행들동.Wpf Nuget 패키지를 프로젝트에 가져옵니다.
    https://www.nuget.org/packages/.Xaml.Behaviors.Wpf/https://www.nuget.org/packages/Microsoft.Xaml.Behaviors.Wpf/
  2. xml에 xmlns:sysours="http://schemas.microsoft.com/xaml/behaviors " 네임스페이스를 추가합니다.

그럼 이렇게 사용하시면 됩니다.

<Button Width="150" Style="{DynamicResource MaterialDesignRaisedDarkButton}">
   <behaviours:Interaction.Triggers>
       <behaviours:EventTrigger EventName="Click">
           <behaviours:InvokeCommandAction Command="{Binding OpenCommand}" PassEventArgsToCommand="True"/>
       </behaviours:EventTrigger>
    </behaviours:Interaction.Triggers>
    Open
</Button>

이벤트 인수를 Command=로 전달True"를 True로 설정해야 하며 구현하는 Relay 명령어는 RoutedEventArgs 또는 개체를 템플릿으로 사용할 수 있습니다.개체를 매개 변수 유형으로 사용하는 경우 적절한 이벤트 유형에 개체를 캐스팅합니다.

명령은 이렇게 보일 것입니다.

OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });

명령 방법은 다음과 같습니다.

private void OnOpenClicked(object parameter)
{
    Logger.Info(parameter?.GetType().Name);
}

'매개 변수'는 라우팅된 이벤트 개체가 됩니다.

그리고 당신이 궁금할 경우를 대비해 로그를

2020-12-15 11:40:36.3600|INFO|내 애플리케이션.모델 보기.주 창 보기 모델|라우팅된 이벤트 인수

로그에 기록된 TypeName은 RoutedEventArgs입니다.

릴레이 명령 구현은 여기에서 확인할 수 있습니다.

릴레이 명령을 사용해야 하는 이유

PS: 모든 컨트롤의 이벤트에 바인딩할 수 있습니다.Window의 Closing 이벤트처럼 해당 이벤트를 얻을 수 있습니다.

<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}">

</ListBox>

명령

{eb:EventBinding}(명령을 찾는 단순 이름 지정 패턴)

{eb:EventBinding Command=CommandName}

명령 매개 변수

$e(이벤트 Aggrs)

이것 또는 이것.소유물

https://github.com/JonghoL/EventBindingMarkup

는 이 질문의 최고 답을 따를 것입니다.

기본적으로 보기 모델에는 모든 항목의 목록과 선택한 항목의 목록이 포함됩니다.그런 다음 선택한 항목의 목록을 관리하는 동작을 목록 상자에 첨부할 수 있습니다.

이렇게 하는 것은 코드 뒤에 아무것도 없다는 것을 의미하며 xaml은 매우 쉽게 따라할 수 있으며, 동작은 앱의 다른 곳에서도 재사용될 수 있습니다.

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />

사용자 지정 사용자 제어 이벤트를 바인딩해야 하는 경우 Interactivity 트리거를 통해 명령에 바인딩 이벤트 솔루션이 작동하지 않는 경우가 있습니다.이 경우 사용자 지정 동작을 사용할 수 있습니다.

바인딩 동작 선언:

public class PageChangedBehavior
{
    #region Attached property

    public static ICommand PageChangedCommand(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(PageChangedCommandProperty);
    }
    public static void SetPageChangedCommand(DependencyObject obj, ICommand value)
    {
        obj.SetValue(PageChangedCommandProperty, value);
    }

    public static readonly DependencyProperty PageChangedCommandProperty =
        DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior),
            new PropertyMetadata(null, OnPageChanged));

    #endregion

    #region Attached property handler

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as PageControl;
        if (control != null)
        {
            if (e.NewValue != null)
            {
                control.PageChanged += PageControl_PageChanged;
            }
            else
            {
                control.PageChanged -= PageControl_PageChanged;
            }
        }
    }

    static void PageControl_PageChanged(object sender, int page)
    {
        ICommand command = PageChangedCommand(sender as DependencyObject);

        if (command != null)
        {
            command.Execute(page);
        }
    }

    #endregion

}

그런 다음 xaml의 명령에 바인딩합니다.

        <controls:PageControl
            Grid.Row="2"
            CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}"
            behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}">
        </controls:PageControl>

@Cameron MacFarland가 언급했듯이, 저는 단순히 뷰 모델의 속성에 양방향 바인딩합니다.속성 설정기에서는 요구 사항에 따라 연락처 목록에 추가하는 등 필요한 모든 논리를 수행할 수 있습니다.

하지만 속성을 반드시 '선택됨'이라고 부르지는 않을 것입니다.뷰 모델은 뷰 도면층과 뷰 도면층의 속성과 상호 작용하는 방식을 알지 못합니다.Current Contact 같은 거요.

분명히 이것은 연습 등을 위한 연습으로 명령어를 만들고 싶지 않은 경우입니다.

이는 다음을 사용한 구현입니다.MarkupExtension낮은 수준의 특성에도 불구하고(이 시나리오에서는 필요함) XAML 코드는 매우 간단합니다.

XAML

<SomeControl Click="{local:EventBinding EventToCommand}" CommandParameter="{local:Int32 12345}" />

마루프 확장

public class EventBindingExtension : MarkupExtension
{
    private static readonly MethodInfo EventHandlerImplMethod = typeof(EventBindingExtension).GetMethod(nameof(EventHandlerImpl), new[] { typeof(object), typeof(string) });
    public string Command { get; set; }

    public EventBindingExtension()
    {
    }
    public EventBindingExtension(string command) : this()
    {
        Command = command;
    }

    // Do not use!!
    public static void EventHandlerImpl(object sender, string commandName)
    {
        if (sender is FrameworkElement frameworkElement)
        {
            object dataContext = frameworkElement.DataContext;

            if (dataContext?.GetType().GetProperty(commandName)?.GetValue(dataContext) is ICommand command)
            {
                object commandParameter = (frameworkElement as ICommandSource)?.CommandParameter;
                if (command.CanExecute(commandParameter)) command.Execute(commandParameter);
            }
        }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider &&
            targetProvider.TargetObject is FrameworkElement targetObject &&
            targetProvider.TargetProperty is MemberInfo memberInfo)
        {
            Type eventHandlerType;
            if (memberInfo is EventInfo eventInfo) eventHandlerType = eventInfo.EventHandlerType;
            else if (memberInfo is MethodInfo methodInfo) eventHandlerType = methodInfo.GetParameters()[1].ParameterType;
            else return null;

            MethodInfo handler = eventHandlerType.GetMethod("Invoke");
            DynamicMethod method = new DynamicMethod("", handler.ReturnType, new[] { typeof(object), typeof(object) });

            ILGenerator ilGenerator = method.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg, 0);
            ilGenerator.Emit(OpCodes.Ldstr, Command);
            ilGenerator.Emit(OpCodes.Call, EventHandlerImplMethod);
            ilGenerator.Emit(OpCodes.Ret);

            return method.CreateDelegate(eventHandlerType);
        }
        else
        {
            throw new InvalidOperationException("Could not create event binding.");
        }
    }
}

언급URL : https://stackoverflow.com/questions/4897775/wpf-binding-ui-events-to-commands-in-viewmodel

반응형