2012-06-20 29 views
1

我認爲這個代碼是不言自明的。我的ViewModel知道視圖,我該如何解決這個問題?

<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(); 
} 

而且我知道它是多麼糟糕到必須在視圖模型視圖邏輯。我能想到沒有更好的辦法,有沒有人建議?也許你會看到使用框架MVVMLight。

+0

從你展示的代碼中,ViewModel中沒有「視圖邏輯」。您的視圖模型並不知道視圖,它只知道它有一個名爲OnDeleting的方法,它是執行時的命令處理程序。我不確定你在擔心什麼。你有錯誤嗎?命令是否正在發射? – Thelonias

+0

是的everythink偉大的作品,即時通訊如果我的編程風格是正確的。我想學習乾淨的編碼,我的感覺是,將視圖eventargs傳遞給視圖模型並不那麼幹淨。是嗎? – slopsucker

回答

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}" /> 

現在,您的視圖模型沒有引用視圖特定的類型。

+0

但這看起來不像OP所要求的。當他的控件的「刪除」事件觸發時,@slopsucker需要一個命令來觸發,這就是他或她已經在做的事情。關注似乎是有視圖邏輯,其中我沒有看到任何(視圖模型中沒有引用特定的控件或它們的屬性)。 – Thelonias

+0

@Ryan true,我沒有考慮到事件處理程序 –

+0

對,我的問題是隻有在這個控件(Control.Map.dll)中的特定MapDeletingEventArgs,如果這還不是一個實現在視圖模型中查看 – slopsucker

0

如果你不想你的手EventArgs的給您的視圖模型,你可以嘗試使用行爲(這類似於史蒂夫Greatrex的解決方案,而是採用了混合SDK'a行爲代替):

這裏是我在其中一個應用程序中使用的示例。

首先,這是我的自定義行爲的基類:

/// <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的「click」事件

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> 

現在,在你的情況下,而不是將NULL傳遞給命令的執行方法d,你想要傳遞你的論點的地圖集合