9

WPFで最初のMVVMアプリケーションを作成するのに忙しいです。

基本的に私が抱えている問題は、WPFウィンドウに配置したTreeView(System.Windows.Controls.TreeView)があり、CommandViewModelアイテムのReadOnlyCollectionにバインドすることを決定したことです。これらのアイテムは、 DisplayString、Tag、およびRelayCommand。

これでXAMLに、TreeViewがあり、ReadOnlyCollectionをこれに正常にバインドしました。私はこれを見ることができ、UIですべてがうまく見えます。

ここでの問題は、RelayCommandをTreeViewItemのコマンドにバインドする必要があることですが、TreeViewItemにコマンドがないことがわかります。これにより、IsSelectedプロパティで、またはTreeView_SelectedItemChangedメソッドの背後にあるコードでそれを実行するように強制されますか?それともWPFでこれを魔法のように実行する方法がありますか?

これは私が持っているコードです:

<TreeView BorderBrush="{x:Null}" 
      HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch">
<TreeView.Items>
    <TreeViewItem
        Header="New Commands"
        ItemsSource="{Binding Commands}"
        DisplayMemberPath="DisplayName"
        IsExpanded="True">
    </TreeViewItem>
</TreeView.Items>

そして理想的には私はただ行きたいです:

<TreeView BorderBrush="{x:Null}" 
      HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch">
<TreeView.Items>
    <TreeViewItem
        Header="New Trade"
        ItemsSource="{Binding Commands}"
        DisplayMemberPath="DisplayName"
        IsExpanded="True"
        Command="{Binding Path=Command}">
    </TreeViewItem>
</TreeView.Items>

誰かが私が持っているRelayCommandインフラストラクチャを使用できるようにするソリューションを持っていますか?

みんなありがとう、大いに感謝します!

リチャード

4

6 に答える 6

28

これが少し前に「回答」されたことは知っていますが、回答が理想的ではなかったので、2 セントを投入することにしました。私は、「スタイル ボタンのトリッキー」に頼ったり、コード ビハインドを使用したりする必要がなく、代わりにすべての分離を MVVM に保持できる方法を使用しています。TreeView に次の xaml を追加します。

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectedItemChanged">
        <i:InvokeCommandAction Command="{Binding TreeviewSelectedItemChanged}" CommandParameter="{Binding ElementName=treeView, Path=SelectedItem}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

xaml ヘッダーに次を追加します。

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

次に、プロジェクトで上記のアセンブリへの参照を追加する必要があります。

その後は、ボタンなどの他のコマンドと同じようにすべてが機能します。

于 2011-02-04T18:05:20.530 に答える
2

問題への情報提供に感謝します。はい、コード ビハインド ソリューションは必要ないと言いましたが、その時点では、単に何かが足りないという印象をまだ強く受けていました。 TreeView_SelectedItemChanged イベント。

Will のアプローチは良い回避策のように思えますが、私の個人的な状況では、コード ビハインドを使用することにしました。この理由は、View と XAML が、TreeViewItem に Command をバインドできる "Command" プロパティがある場合と同じように残るようにするためです。テンプレートやビューを変更する必要はありません。コードと TreeView_SelectedItemChanged のイベントを追加するだけです。

私の解決策:

  private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (sender != null)
        {
            var treeView = sender as TreeView;
            if (treeView != null)
            {
                var commandViewModel = treeView.SelectedItem as CommandViewModel;
                if (commandViewModel != null)
                {
                    var mi = commandViewModel.Command.GetType().GetMethod("Execute");
                    mi.Invoke(commandViewModel.Command, new Object[] {null});
                }
            }
        }
    }

既に RelayCommand が TreeViewItem にアタッチされているので、今行っていることは、その特定の RelayCommand で「実行」メソッドを手動で呼び出すことだけです。

これが完全に間違った方法である場合は、お知らせください...

ありがとう!

于 2010-02-15T19:31:24.833 に答える
1

私がすることは、TreeViewItemのヘッダーをボタンに設定し、ボタンの外観や動作がないようにボタンをスキンしてから、ボタンに対してコマンド バインディングを実行することです。

DataTemplate を介してこれを行う必要がある場合や、TreeViewItem 自体のテンプレートを変更する必要がある場合があります。やったことはありませんが、これは私が似たようなことをした方法です(タブページのヘッダーなど)。


これが私が話していることの例です(これをKaxamlにドロップして、いじることができます):

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Page.Resources>
      <Style x:Key="ClearButan" TargetType="Button">
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="Button">              
                 <Border Name="border"
                     Padding="4"
                     Background="transparent">
                     <Grid >
                     <ContentPresenter HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                     </ContentPresenter>
                     </Grid>
                 </Border>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
   </Page.Resources>
   <Grid>
      <TreeView>
         <TreeViewItem>
            <Button Style="{StaticResource ClearButan}">
            easy peasy
            </Button>
         </TreeViewItem>
      </TreeView>
   </Grid>
</Page>

ボタンの新しい明確なスタイルを作成しました。次に、TVI にボタンをドロップして、そのスタイルを設定します。もちろん、データ テンプレートを使用して同じことを行うことができます。

于 2010-02-15T15:29:03.367 に答える
0

Shaggy13spe が提供する回答は非常に優れています。それでも、それを理解するのにさらに時間がかかったので、答えを広げます。

TreeView xaml 全体は次のようになります。

<TreeView x:Name="treeView" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Tree}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <i:InvokeCommandAction Command="{Binding FilterMeetingsCommand}" CommandParameter="{Binding  ElementName=treeView, Path=SelectedItem}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"></TextBlock>
                <TextBlock Text="{Binding Id}"></TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

私のビューにはTreeコレクションがあります

public ObservableCollection<TreeNode> Tree { get; set; }

TreeNodeは単純なクラスとして定義されています。

public class TreeNode
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<TreeNode> Nodes { get; set; }

    public TreeNode(string name)
    {
        this.Name = name;
        this.Nodes = new List<TreeNode>();
    }
}

最初の重要なポイント: CommandParameterは ViewModel のプロパティにバインドされませんが、メソッドに渡されます。したがって、メソッドは次のようになります。

private async void FilterMeeting(object parameter){}

2 番目の重要なポイント: 選択したアイテム (私の場合、オブジェクトはTreeNodeタイプ) を渡し、階層構造を持つ場合、イベント バブリングに直面します。したがって、アイテムを選択すると、この特定のアイテムとすべての親に対してイベントが発生します。これを解決するには、 ViewModelのメソッドに渡すことができるオブジェクトは 1 つだけ(標準のイベント ハンドラーのように 2 つではありません) であり、このオブジェクトはイベントである必要があることを理解する必要があります。

この場合、XAML を次のように変更します (ここではPassEventArgsToCommand="True"が重要です) 。

<TreeView x:Name="treeView" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Tree}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
            <i:InvokeCommandAction Command="{Binding FilterMeetingsCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"></TextBlock>
                <TextBlock Text="{Binding Id}"></TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

次に、処理メソッドで、モデル オブジェクトを受け取りませんが、モデル オブジェクトを内部に持つイベント引数を受け取ります。

選択した要素を持つイベント

于 2020-07-07T22:11:00.520 に答える