1

コードは自明だと思います。

<Interactivity:Interaction.Triggers>
    <Interactivity:EventTrigger EventName="Deleting">
        <MVVMLight:EventToCommand Command="{Binding Deleting, Mode=OneWay}" 
                                  PassEventArgsToCommand="True" />
    </Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>

削除イベントを持つ独自のカスタム コントロールがあり、それを ViewModel のコマンドにバインドしたいと考えています。

しかし、ビューモデルでは、私は今どちらかを持っています

public void OnDeleting(EventArgs args)
{
    var e = args as MapDeletingEventArgs;

    if (e == null) 
        throw new ArgumentNullException("args");

    Database.Delete(e.Maps);
    Database.Commit();
}

またはさらに悪い

public void OnDeleting(MapDeletingEventArgs args)
{
    if (args == null) 
        throw new ArgumentNullException("args");

    Database.Delete(args.Maps);
    Database.Commit();
}

そして、ViewModel にビュー ロジックを含めることがいかに悪いことかを知っています。これ以上の方法は考えられません。誰かアドバイスはありますか? ご覧のとおり、フレームワーク MVVMLight を使用しています。

4

2 に答える 2

2

これは、コマンドパラメータとしてインスタンスを使用するICommand実装で実現できます。Map

//WARNING: all code typed in SO window
public class DeleteMapsCommand : ICommand
{
    private Database _db;

    public DeleteMapsCommand(Database db)
    {
        _db = db;
    }

    public void CanExecute(object parameter)
    {
        //only allow delete if the parameter passed in is a valid Map
        return (parameter is Map);
    }

    public void Execute(object parameter)
    {
        var map = parameter as Map;
        if (map == null) return;

        _db.Delete(map);
        _db.Commit();
    }

    public event EventHandler CanExecuteChanged; //ignore this for now
}

次に、ビューモデルにパブリックプロパティを作成して、コマンドのインスタンスを公開します

public class ViewModel
{
    public ViewModel() {
        //get the Database reference from somewhere?
        this.DeleteMapCommand = new DeleteMapsCommand(this.Database); 
    }

    public ICommand DeleteMapCommand { get; private set; }
}

最後に、アクションをコマンドプロパティバインドし、コマンドプロパティを削除するマップにバインドする必要があります。あなたは、あなたのケースでこれがどのように行われるべきかを述べるのに十分なXAMLを私に与えていませんが、以下のようなことをListBox:で行うことができます。

<ListBox x:Name="ListOfMaps" ItemsSource="{Binding AllTheMaps}" />
<Button Command="{Binding DeleteMapCommand}" CommandParameter="{Binding SelectedItem, ElementName=ListOfMaps}">Delete Selected Map</Button>

アップデート

コマンドをイベントにアタッチするには、アタッチされたプロパティを使用できます。

public static class Helper
{
    public static IComparable GetDeleteMapCommand(DependencyObject obj)
    {
        return (IComparable)obj.GetValue(DeleteMapCommandProperty);
    }

    public static void SetDeleteMapCommand(DependencyObject obj, IComparable value)
    {
        obj.SetValue(DeleteMapCommandProperty, value);
    }

    public static readonly DependencyProperty DeleteMapCommandProperty =
        DependencyProperty.RegisterAttached("DeleteMapCommand", typeof(IComparable), typeof(Helper), new UIPropertyMetadata(null, OnDeleteMapCommandChanged));

    private static void OnDeleteMapCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        //when we attach the command, grab a reference to the control
        var mapControl = sender as MapControl;
        if (mapControl == null) return;

        //and the command
        var command = GetDeleteMapCommand(sender);
        if (command == null) return;

        //then hook up the event handler
        mapControl.Deleting += (o,e) =>
        {
            if (command.CanExecute(e.Maps))
                command.Execute(e.Maps);
        };
    }
}

次に、次のようにコマンドをバインドする必要があります。

<MapControl local:Helper.DeleteMapCommand="{Binding DeleteMapCommand}" />

これで、ビューモデルにはビュー固有のタイプへの参照がなくなりました。

于 2012-06-20T16:15:12.397 に答える
0

EventArgsをビューモデルに渡したくない場合は、ビヘイビアを使用してみてください(これは、Steve Greatrexのソリューションに似ていますが、代わりにBlend SDKのビヘイビアを使用します)。

これは、私のアプリケーションの1つで使用する例です。

まず、これが私のカスタム動作の基本クラスです。

/// <summary>
/// "Better" Behavior base class which allows for safe unsubscribing. The default Behavior class does not always call <see cref="Behavior.OnDetaching"/>
/// </summary>
/// <typeparam name="T">The dependency object this behavior should be attached to</typeparam> 
public abstract class ZBehaviorBase<T> : Behavior<T> where T : FrameworkElement
{
    private bool _isClean = true;

    /// <summary>
    /// Called after the behavior is attached to an AssociatedObject.
    /// </summary>
    /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
    protected sealed override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Unloaded += OnAssociatedObjectUnloaded;
        _isClean = false;     
        ValidateRequiredProperties();     
        Initialize();
    }

    /// <summary>
    /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
    /// </summary>
    /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
    protected sealed override void OnDetaching()
    {
        CleanUp();     
        base.OnDetaching();
    }

    /// <summary>
    /// Validates the required properties. This method is called when the object is attached, but before
    /// the <see cref="Initialize"/> is invoked.
    /// </summary>
    protected virtual void ValidateRequiredProperties()
    {
    }

    /// <summary>
    /// Initializes the behavior. This method is called instead of the <see cref="OnAttached"/> which is sealed
    /// to protect the additional behavior.
    /// </summary>
    protected abstract void Initialize();        

    /// <summary>
    /// Uninitializes the behavior. This method is called when <see cref="OnDetaching"/> is called, or when the
    /// <see cref="AttachedControl"/> is unloaded.
    /// <para />
    /// If dependency properties are used, it is very important to use <see cref="ClearValue"/> to clear the value
    /// of the dependency properties in this method.
    /// </summary>
    protected abstract void Uninitialize();

    /// <summary>
    /// Called when the <see cref="AssociatedObject"/> is unloaded.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    private void OnAssociatedObjectUnloaded(object sender, EventArgs e)
    {
        CleanUp();
    }

    /// <summary>
    /// Actually cleans up the behavior because <see cref="OnDetaching"/> is not always called.
    /// </summary>
    /// <remarks>
    /// This is based on the blog post: http://dotnetbyexample.blogspot.com/2011/04/safe-event-detachment-pattern-for.html.
    /// </remarks>
    private void CleanUp()
    {
        if (_isClean)
        {
            return;
        }

        _isClean = true;

        if (AssociatedObject != null)
        {
            AssociatedObject.Unloaded -= OnAssociatedObjectUnloaded;
        }

        Uninitialize();
    }
}

さて、私の具体的な実装は、TextBlockの「クリック」イベントにコマンドを添付するために使用されました

public class TextBlockClickCommandBehavior : ZBehaviorBase<TextBlock>
{
    public ICommand ClickCommand
    {
        get { return (ICommand)GetValue(ClickCommandProperty); }
        set { SetValue(ClickCommandProperty, value); }
    }

    public static readonly DependencyProperty ClickCommandProperty =
        DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(TextBlockClickCommandBehavior));

    protected override void Initialize()
    {
        this.AssociatedObject.MouseLeftButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
    }

    protected override void Uninitialize()
    {
        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.MouseLeftButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
        }
    }

    void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        // if you want to pass a command param to CanExecute, need to add another dependency property to bind to
        if (ClickCommand != null && ClickCommand.CanExecute(null))
        {
            ClickCommand.Execute(null);
        }
    }
}

そして、私はそれを次のように使用します:

<!--Make the TextBlock for "Item" clickable to display the item search window-->
<TextBlock x:Name="itemTextBlock" Text="Item:" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="2" FontWeight="Bold">
    <e:Interaction.Behaviors>
        <ZViewModels:TextBlockClickCommandBehavior ClickCommand="{Binding Path=ItemSearchCommand}"/>
    </e:Interaction.Behaviors>
</TextBlock>

さて、あなたの場合、コマンドのexecuteメソッドにNULLを渡す代わりに、引数のMapsコレクションを渡したいと思います。

于 2012-06-20T17:23:57.033 に答える