web-dev-qa-db-ja.com

MVVMを使用したWPFのTextBoxからの適切なDataGrid検索

私はMVVMパターンに不慣れで、コードビハインドをいつ使用するかについて少し混乱しています。現在、1つのTextBoxと1つのDataGridを含む非常に単純なフォームがあります。私が望むのは、DataGridがTextBoxに基づいて選択されたアイテムを変更できるようにすることです。

私はこれをコードビハインドで行い、次のコードを使用して正常に動作します。

private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
    for (int i = 0; i < dataGrid1.Items.Count; i++)
    {
        string cellContent = dtReferral.Rows[i][0].ToString();
        try
        {
            if (cellContent != null && cellContent.Substring(0, textBox1.Text.Length).Equals(textBox1.Text))
            {
                object item = dataGrid1.Items[i];
                dataGrid1.SelectedItem = item;
                dataGrid1.ScrollIntoView(item);
                //row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                break;
            }
        }
        catch { }
    }
}

ここで、テキストボックス内のテキストで始まるDatagrid内のアイテムを強調表示し、ユーザーがボタンを押して選択したアイテムを編集できるようにします。

このロジックを分離コードファイルに含めることはできますか?それとも、何らかのバインドによってこれを行う必要がありますか?バインディング付きのビューモデルを使用してこれを行う必要がある場合は、任意の方向でいただければ幸いです。ありがとうございました。

18
Daryl Behrens

TextBoxからのテキストでセルを強調表示するだけの場合、AttatchedPropertyDataGridを作成してTextBoxから検索値を受け入れ、AttatchedPropertyの別のCellを作成して、プロパティで設定できる一致を示すことができます。 Cellスタイル。次に、IMultiValueConverterを作成して、Cellの値が検索Textと一致するかどうかを確認します。

このように、AttachedPropertiesConverterだけが必要なので、他のプロジェクトで再利用できます

AttachedPropertySearchValueTextBoxTextプロパティにバインドします。

 <DataGrid local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}" 

次に、StyleDataGridCellを作成し、AttachedPropertyを使用してIsTextMatchのセッターを作成し、IMultiValueConverterを使用して、セルのテキストがSearchValueと一致するかどうかを返します。

<Setter Property="local:DataGridTextSearch.IsTextMatch">
    <Setter.Value>
        <MultiBinding Converter="{StaticResource SearchValueConverter}">
            <Binding RelativeSource="{RelativeSource Self}" Path="Content.Text" />
            <Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
        </MultiBinding>
    </Setter.Value>
</Setter>

次に、Cells添付のIsTextMatchプロパティを使用して、Triggerを使用してハイライトを設定できます。

<Style.Triggers>
    <Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
        <Setter Property="Background" Value="Orange" />
    </Trigger>
</Style.Triggers>

ここに私の散歩を示す実用的な例があります:)

コード:

namespace WpfApplication17
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            for (int i = 0; i < 20; i++)
            {
                TestData.Add(new TestClass { MyProperty = GetRandomText(), MyProperty2 = GetRandomText(), MyProperty3 = GetRandomText() });
            }
        }

        private string GetRandomText()
        {
            return System.IO.Path.GetFileNameWithoutExtension(System.IO.Path.GetRandomFileName());
        }

        private ObservableCollection<TestClass> _testData = new ObservableCollection<TestClass>();
        public ObservableCollection<TestClass> TestData
        {
            get { return _testData; }
            set { _testData = value; }
        }
    }

    public class TestClass
    {
        public string MyProperty { get; set; }
        public string MyProperty2 { get; set; }
        public string MyProperty3 { get; set; }
    }

    public static class DataGridTextSearch
    {
        // Using a DependencyProperty as the backing store for SearchValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SearchValueProperty =
            DependencyProperty.RegisterAttached("SearchValue", typeof(string), typeof(DataGridTextSearch),
                new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits));

        public static string GetSearchValue(DependencyObject obj)
        {
            return (string)obj.GetValue(SearchValueProperty);
        }

        public static void SetSearchValue(DependencyObject obj, string value)
        {
            obj.SetValue(SearchValueProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsTextMatch.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsTextMatchProperty =
            DependencyProperty.RegisterAttached("IsTextMatch", typeof(bool), typeof(DataGridTextSearch), new UIPropertyMetadata(false));

        public static bool GetIsTextMatch(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsTextMatchProperty);
        }

        public static void SetIsTextMatch(DependencyObject obj, bool value)
        {
            obj.SetValue(IsTextMatchProperty, value);
        }
    }

    public class SearchValueConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string cellText = values[0] == null ? string.Empty : values[0].ToString();
            string searchText = values[1] as string;

            if (!string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(cellText))
            {
                return cellText.ToLower().StartsWith(searchText.ToLower());
            }
            return false;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
}

XAML:

<Window x:Class="WpfApplication17.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication17"
        Title="MainWindow" Height="350" Width="525" Name="UI">

    <StackPanel DataContext="{Binding ElementName=UI}">
        <TextBox Name="SearchBox" />
        <DataGrid x:Name="grid" local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}" 
                  ItemsSource="{Binding TestData}" >
            <DataGrid.Resources>
                <local:SearchValueConverter x:Key="SearchValueConverter" />
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="local:DataGridTextSearch.IsTextMatch">
                        <Setter.Value>
                            <MultiBinding Converter="{StaticResource SearchValueConverter}">
                                <Binding RelativeSource="{RelativeSource Self}" Path="Content.Text" />
                                <Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
                            <Setter Property="Background" Value="Orange" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.Resources>
        </DataGrid>
    </StackPanel>
</Window>

結果:

enter image description hereenter image description here

編集:

単一の列に基づいて行を選択するだけの場合は、非常に簡単に変更できます。

DataGridRowの代わりにDataGridCellのスタイルをオーバーライドします。

  <Style TargetType="{x:Type DataGridRow}">

最初に、必要なプロパティをIMultiValueConverterに渡します。これはDataContextである必要があります

<MultiBinding Converter="{StaticResource SearchValueConverter}">
    <Binding RelativeSource="{RelativeSource Self}" Path="DataContext.MyProperty" />
    <Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
</MultiBinding>

次に、Triggerを変更して、IsSelectedRowを設定します

<Style.Triggers>
    <Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
        <Setter Property="IsSelected" Value="True" />
    </Trigger>
</Style.Triggers>

次のようになります。

 <DataGrid x:Name="grid" local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}" 
              ItemsSource="{Binding TestData}" >
        <DataGrid.Resources>
            <local:SearchValueConverter x:Key="SearchValueConverter" />
            <Style TargetType="{x:Type DataGridRow}">
                <Setter Property="local:DataGridTextSearch.IsTextMatch">
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource SearchValueConverter}">
                            <Binding RelativeSource="{RelativeSource Self}" Path="DataContext.MyProperty" />
                            <Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
                        <Setter Property="IsSelected" Value="True" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </DataGrid.Resources>
    </DataGrid>

結果:

enter image description here

39
sa_ddam213

私はしばらくの間MVVMを静かに使用してきましたが、MVVMパターンですべてを正確に実行することが常に実用的であるとは限らないため、厳密な実践よりもガイドラインとしてatを使用することを好みます。それに慣れる。
あなたにぴったりのMVVMの形が見つかるまで、いじってみることをお勧めします。
コードがUI関連である場合、MVVMのコードビハインドにコードを含めることはタブーだとは思わない。
ScrollIntoViewはBindableプロパティではないため、それにバインドする場合は、バインドを間接的に処理するための依存関係プロパティを作成する必要があります。選択したアイテムの設定に関しては、次のような方法で行うことができます。

見る:

<TextBox Height="23" Text={Binding Path=Selected, UpdateSourceTrigger=PropertyChanged} HorizontalAlignment="Left" Margin="90,147,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
<DataGrid AutoGenerateColumns="True" 
          ItemsSource="{Binding Path=ItemList}" 
          SelectedItem="{Binding Path=Selected}" >
</DataGrid>

ViewModel:

 private string _selected = "";
 public string Selected
 {
      get{ return _selected; }
      set
      {
            if(_selected == value) return;
            _selected = value;
            base.OnPropertyChanged("Selected");
      }
 }
0
Heinrich