2016-12-03 16 views
0

以前はVS addinにあった機能のVSパッケージ拡張を開発中です。WPF - Listbox項目の2つのコマンド

アドインはツールバーのウィンドウにファイルをロードし、ユーザがアイテム(ファイルの名前)をダブルクリックすると、ファイルはVSのエディタで開きます。ユーザが右クリックした場合アイテムにポップアップメニューが表示されます。ですから、私の質問は、リストボックス項目のこれらのアクション(ダブルクリックと右クリック)を既存のコードと結びつけるのが最善の方法です。

私たちはWPFを使用していますが、アドインの場合はWindowsフォームでした。 しかし、私はWPFにあまり慣れていません。約1年前、私はブライアン・ノイエスのPluralsightコース「WPF MVVM In Depth」を見て、エクステンションにいくつかのことを実装しましたが、今年のほとんどの期間、エクステンションに取り組んでいません。結果は、私が書いたコードの曖昧な想い出しか持たないということです。私は、最高のデザインが何であるかについてちょっと混乱しています。

だから、私は私がすでに持っているものをお見せしましょう:

ここでXAMLファイルされる:

<UserControl x:Class="Sym.VisualStudioExtension.Engines.TAEngineView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:behaviours="clr-namespace:Sym.VisualStudioExtension" 
     xmlns:local="clr-namespace:Sym.VisualStudioExtension" 
     local:ViewModelLocator.AutoWireViewModel="True" 
     mc:Ignorable="d" 
     d:DesignHeight="700" d:DesignWidth="400"> 
<Grid> 
    <TabControl x:Name="tabControl" HorizontalAlignment="Left" Height="490" Margin="19,44,-36,-234" VerticalAlignment="Top" Width="317"> 
     <TabItem Header="Parameter Files"> 
      <ListBox Margin="20" ItemsSource="{Binding ParameterFilesList}"> 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <TextBlock Text="{Binding Path=Name}" /> 
         </StackPanel> 
        </DataTemplate> 
       </ListBox.ItemTemplate>      
      </ListBox> 
     </TabItem> 
     <TabItem Header="Calc Files"> 
      <ListBox Margin="20" ItemsSource="{Binding CalcFilesList}"> 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <TextBlock Text="{Binding Path=Name}" /> 
         </StackPanel> 
        </DataTemplate> 
       </ListBox.ItemTemplate>     
      </ListBox> 
     </TabItem> 
    </TabControl> 
    <Label x:Name="label" Content="{Binding Path=Title}" HorizontalAlignment="Left" Margin="19,13,0,0" VerticalAlignment="Top" Width="367 
      " BorderThickness="2"/> 

</Grid> 

CalcFilesListはタイプObservableCollection<CalcFile>のものであり、タイプObservableCollection<Parameter>のParameterFilesList。

その後、私はすでにこのRelayCommandクラスを持っている:

using System; 
using System.Diagnostics; 
using System.Windows.Input; 

namespace Sym.VisualStudioExtension 
{ 
/// <summary> 
/// A command whose sole purpose is to 
/// relay its functionality to other 
/// objects by invoking delegates. The 
/// default return value for the CanExecute 
/// method is 'true'. 
/// </summary> 
public class RelayCommand : ICommand 
{ 
    #region Fields 

    readonly Action<object> _execute; 
    readonly Predicate<object> _canExecute;   

    #endregion // Fields 

    #region Constructors 

    /// <summary> 
    /// Creates a new command that can always execute. 
    /// </summary> 
    /// <param name="execute">The execution logic.</param> 
    public RelayCommand(Action<object> execute) 
     : this(execute, null) 
    { 
    } 

    /// <summary> 
    /// Creates a new command. 
    /// </summary> 
    /// <param name="execute">The execution logic.</param> 
    /// <param name="canExecute">The execution status logic.</param> 
    public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 

     _execute = execute; 
     _canExecute = canExecute;   
    } 

    #endregion // Constructors 

    #region ICommand Members 

    [DebuggerStepThrough] 
    public bool CanExecute(object parameters) 
    { 
     return _canExecute == null ? true : _canExecute(parameters); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameters) 
    { 
     _execute(parameters); 
    } 

    #endregion // ICommand Members 
} 

public class RelayCommand<T> : ICommand 
    { 
     #region Fields 

     private readonly Action<T> _execute = null; 
     private readonly Predicate<T> _canExecute = null; 

     #endregion 

     #region Constructors 

     /// <summary> 
     /// Creates a new command that can always execute. 
     /// </summary> 
     /// <param name="execute">The execution logic.</param> 
     public RelayCommand(Action<T> execute) 
      : this(execute, null) 
     { 
     } 

     /// <summary> 
     /// Creates a new command with conditional execution. 
     /// </summary> 
     /// <param name="execute">The execution logic.</param> 
     /// <param name="canExecute">The execution status logic.</param> 
     public RelayCommand(Action<T> execute, Predicate<T> canExecute) 
     { 
      if (execute == null) 
       throw new ArgumentNullException("execute"); 

      _execute = execute; 
      _canExecute = canExecute; 
     } 

     #endregion 

     #region ICommand Members 

     public bool CanExecute(object parameter) 
     { 
      return _canExecute == null ? true : _canExecute((T)parameter); 
     } 

     public event EventHandler CanExecuteChanged 
     { 
      add 
      { 
       if (_canExecute != null) 
        CommandManager.RequerySuggested += value; 
      } 
      remove 
      { 
       if (_canExecute != null) 
        CommandManager.RequerySuggested -= value; 
      } 
     } 

     public void Execute(object parameter) 
     { 
      _execute((T)parameter); 
     } 

     #endregion 
    } 
} 

そして、このBindableBaseクラス:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading.Tasks; 

namespace Sym.VisualStudioExtension 
{ 
    public class BindableBase : INotifyPropertyChanged 
    { 
     protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null) 
     { 
      if (object.Equals(member, val)) return; 

      member = val; 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
     protected virtual void OnPropertyChanged(string propertyName) 
     {   
       PropertyChanged(this, new  PropertyChangedEventArgs(propertyName));  
     } 

    } 
} 

そして、ここではViewModelLocatorです:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using Microsoft.Practices.Unity; 
using Symplexity.VisualStudioExtension.Engines; 

namespace Sym.VisualStudioExtension 
{ 
    public static class ViewModelLocator 
    { 
     public static bool GetAutoWireViewModel(DependencyObject obj) 
     { 
      return (bool)obj.GetValue(AutoWireViewModelProperty); 
     } 

     public static void SetAutoWireViewModel(DependencyObject obj, bool value) 
     { 
      obj.SetValue(AutoWireViewModelProperty, value); 
     } 

     // Using a DependencyProperty as the backing store for AutoWireViewModel. This enables animation, styling, binding, etc... 
     public static readonly DependencyProperty AutoWireViewModelProperty = 
      DependencyProperty.RegisterAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), new PropertyMetadata(false, AutoWireViewModelChanged)); 

     private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      if (DesignerProperties.GetIsInDesignMode(d)) return; 
      var viewType = d.GetType(); 
      var viewTypeName = viewType.FullName; 
      var viewModelTypeName = viewTypeName + "Model"; 
      var viewModelType = Type.GetType(viewModelTypeName); 


      if (viewModelTypeName.Contains("UtilitiesViewModel")) 
      { 
       UtilitiesViewModel uViewModel = ContainerHelper.Container.Resolve<UtilitiesViewModel>(); 
       ((FrameworkElement)d).DataContext = uViewModel; 
      } 
      else 
      { 
       var viewModel = ContainerHelper.Container.Resolve(viewModelType); 
       ((FrameworkElement)d).DataContext = viewModel; 
      }   
     } 
    } 
} 

私はかなりの数を見てきましたリストボックスの項目やマウスのイベントなどについての他のスレッド。そんなに私は混乱してしまったどの経路に行くか。

Various options

ItemContainerStyles&Commands

私は背後にあるコードで何かを持つことはそれほど悪くないです、そしてそれは私がWPFとMVVMについて知っていた少し忘れている私のような誰かのために非常に簡単に見えると思いますが、 RelayCommand、BindableBase、ViewModelLocatorは既に用意されているので、マウスイベント(ダブルクリックと右クリック)をコマンドと接続するように設計されているように感じられますが、どういうわけかはわかりません。 したがって、VSEエディタでListBox(ダブルクリックされている場合)の項目に名前が表示される、基本ファイルを開くTAEngineViewModelのOpenFileメソッドがあるとしたら、XAMLには何を入れますか? 選択したCalcFile/ParameterFileオブジェクトをTAEngineViewModelに渡すにはどうすればよいですか?

私は、右クリックイベントがダブルクリックと似ていると仮定します。そうでない場合はどうなりますか?

+0

ここで見ることができますhttp://stackoverflow.com/questions/11172443/how-to-fire-a-command-on-double-click-listbox-item-using-mvvm –

答えて

0

私が理解できるように、リストアイテムのクリック/ダブルクリックイベントを受け取り、ビューモデル内の特定のメソッドにリダイレクトするメカニズムが必要です。ここに私の提案は次のとおりです。

  1. がnuget経由Caliburn.Microと呼ばれる近代的で快適なMVVMフレームワークを見つけ、あなたのプロジェクトにそれをインストールします(あなたがパッケージをインストールするnugetコンソールを使用することができ、ここでの説明リンクhttps://www.nuget.org/packages/Caliburn.Microです)、 。
  2. TAEngineViewビューに接続されているビューモデルがない場合は、作成する必要があります。ビューモデルロケータは命名規則に基づいているため、このビューモデルはTAEngineViewModelと呼ばれる必要があります。
  3. ビューモデル内でOnItemClickおよびOnItemDoubleClickというメソッドを作成します。
  4. 特定のキャリバー構文を使用して、クリックイベントをビューモデル内の特定のメソッドにリダイレクトすると、moreコマンドhttps://caliburnmicro.codeplex.com/wikipage?title=Cheat%20Sheet&referringTitle=Documentationとのリンクが表示されます。ここ

ビューモデルコードである:ここ

public class TAEngineViewModel:IOnClickSupport 
{ 
    private readonly ObservableCollection<ItemWithName> _parameterList; 
    public ObservableCollection<ItemWithName> CalcFilesList => _parameterList; 
    public ObservableCollection<ItemWithName> ParameterFilesList => _parameterList; 

    public TAEngineViewModel() 
    { 
     _parameterList = new ObservableCollection<ItemWithName> 
     { 
      new ItemWithName(this) {Name = "AAAAAA"}, 
      new ItemWithName(this) {Name = "BBBBBB"}, 
      new ItemWithName(this) {Name = "CCCCCC"}, 
      new ItemWithName(this) {Name = "DDDDDD"} 
     }; 
    } 

    public void OnClick(object args) 
    { 

    } 

    public void OnDoubleClick(object args) 
    { 

    } 
} 

public interface IOnClickSupport 
{ 
    void OnClick(object args); 
    void OnDoubleClick(object args); 
} 

public class ItemWithName:BaseObservableObject 
{ 
    private readonly IOnClickSupport _support; 
    private string _name; 

    public ItemWithName(IOnClickSupport support) 
    { 
     _support = support; 
    } 

    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      OnPropertyChanged(); 
     } 
    } 

    public void OnClick(object args) 
    { 
     _support.OnClick(args); 
    } 

    public void OnDoubleClick(object args) 
    { 
     _support.OnDoubleClick(args); 
    } 
} 

は、ビューコードである:

<UserControl x:Class="SoCaliburnInvolvedWithEventsToCommand.TAEngineView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:cal="http://www.caliburnproject.org" 
     xmlns:soCaliburnInvolvedWithEventsToCommand="clr-namespace:SoCaliburnInvolvedWithEventsToCommand" 
     soCaliburnInvolvedWithEventsToCommand:ViewModelLocator.AutoWireViewModel="True"> 
<UserControl.Resources> 
    <DataTemplate x:Key="DataTemplateWithTextBlockInside"> 
     <TextBlock Text="{Binding Path=Name}" /> 
    </DataTemplate> 
    <DataTemplate x:Key="ItemTemplate"> 
     <StackPanel Orientation="Horizontal"> 
      <ContentControl cal:Message.Attach="[Event MouseDoubleClick] = [Action OnDoubleClick($eventArgs)];[Event MouseRightButtonDown] = [Action OnClick($eventArgs)]" 
          Content="{Binding}" 
          ContentTemplate="{StaticResource DataTemplateWithTextBlockInside}" /> 
     </StackPanel> 
    </DataTemplate> 
</UserControl.Resources> 
<Grid> 
    <TabControl x:Name="tabControl" 
        Width="317" 
        Height="490" 
        Margin="19,44,-36,-234" 
        HorizontalAlignment="Left" 
        VerticalAlignment="Top"> 
     <TabItem Header="Parameter Files"> 
      <ListBox Margin="20" 
         ItemTemplate="{StaticResource ItemTemplate}" 
         ItemsSource="{Binding ParameterFilesList}" /> 
     </TabItem> 
     <TabItem Header="Calc Files"> 
      <ListBox Margin="20" 
         ItemTemplate="{StaticResource ItemTemplate}" 
         ItemsSource="{Binding CalcFilesList}" /> 
     </TabItem> 
    </TabControl> 
    <Label x:Name="label" 
       Width="367   " 
       Margin="19,13,0,0" 
       HorizontalAlignment="Left" 
       VerticalAlignment="Top" 
       BorderThickness="2" 
       Content="{Binding Path=Title}" /> 

</Grid></UserControl> 

上記溶液の欠点は、下環状関係が存在するという事実であります(主モデルのOnClick/OnDoubleClickロジックを持つサブアイテムをサポートするためにビューモデルが各サブアイテムに配信されます)、イベントアグリゲーションの種類を使用してこの関係を避けることができます。私はあなたにRX拡張を使うことを提案することができます。これは、2つのオブジェクト間を接続する柔軟な方法です。 RXの使用を開始するには、VS2015のinstal it via Nugetを使用してください。ここ

は一例であり:

public class TAEngineViewModel:IDisposable 
{ 
    private IList<IDisposable> _disposablesChildrenList = new List<IDisposable>(); 
    private readonly ObservableCollection<ItemWithName> _parameterList; 
    public ObservableCollection<ItemWithName> CalcFilesList => _parameterList; 
    public ObservableCollection<ItemWithName> ParameterFilesList => _parameterList; 

    public TAEngineViewModel() 
    { 
     _parameterList = new ObservableCollection<ItemWithName> 
     { 
      new ItemWithName {Name = "AAAAAA"}, 
      new ItemWithName {Name = "BBBBBB"}, 
      new ItemWithName {Name = "CCCCCC"}, 
      new ItemWithName {Name = "DDDDDD"} 
     }; 
     Subscribe(_parameterList); 

    } 

    private void Subscribe(ObservableCollection<ItemWithName> parameterList) 
    { 
     foreach (var itemWithName in parameterList) 
     { 
      var onRightClickObservableSubscription = itemWithName.OnRightClickObservable.Subscribe(OnClick); 
      var onDoubleClickObservableSubscription = itemWithName.OnDoubleClickObservable.Subscribe(OnDoubleClick); 
      _disposablesChildrenList.Add(onDoubleClickObservableSubscription); 
      _disposablesChildrenList.Add(onRightClickObservableSubscription); 
     } 
    } 

    public void OnClick(IItemArguments args) 
    { 
     Debug.WriteLine($"{args.SpecificItemWithName.Name} evet {args.SpecificEventArgs.GetType().Name}"); 
    } 

    public void OnDoubleClick(IItemArguments args) 
    { 
     Debug.WriteLine($"{args.SpecificItemWithName.Name} evet {args.SpecificEventArgs.GetType().Name}"); 
    } 

    public void Dispose() 
    { 
     foreach (var disposable in _disposablesChildrenList) 
     { 
      disposable.Dispose(); 
     } 
    } 
} 

public interface IItemArguments 
{ 
    ItemWithName SpecificItemWithName { get;} 
    object SpecificEventArgs { get;} 
} 

public class ItemArguments : IItemArguments 
{ 
    public ItemArguments(ItemWithName item, object args) 
    { 
     SpecificItemWithName = item; 
     SpecificEventArgs = args; 
    } 

    public ItemWithName SpecificItemWithName { get; } 

    public object SpecificEventArgs { get; } 
} 

public class ItemWithName:BaseObservableObject 
{ 
    private string _name; 

    private Subject<IItemArguments> _onDoubleClick = new Subject<IItemArguments>(); 
    private Subject<IItemArguments> _onClick = new Subject<IItemArguments>(); 
    public IObservable<IItemArguments> OnDoubleClickObservable; 
    public IObservable<IItemArguments> OnRightClickObservable; 

    public ItemWithName() 
    { 
     OnDoubleClickObservable = _onDoubleClick.AsObservable(); 
     OnRightClickObservable = _onClick.AsObservable(); 
    } 

    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      OnPropertyChanged(); 
     } 
    } 

    public void OnClick(object args) 
    { 
     _onClick.OnNext(new ItemArguments(this, args)); 
    } 

    public void OnDoubleClick(object args) 
    { 
     _onDoubleClick.OnNext(new ItemArguments(this, args)); 
    } 
} 

あなたが見ることができるように、方法のOnClick/OnDoubleClickが呼び出されるたびに、ビューモデルに渡される特定(選択)項目。 それはすべてです。 詳しい説明が必要な場合はお知らせください。 よろしくお願いいたします。

+0

ありがとうございました単純にコードビハインドを使用していますが、将来この回答に戻る可能性があります。 – Igavshne

関連する問題