正确的DataGrid使用MVVM从WPF中的TextBox中进行search

我是MVVM模式的新手,并且对何时使用Code Behind有点困惑。 我现在有一个非常简单的表单,包括一个TextBox和一个DataGrid。 我想要的是能够让DataGrid基于TextBox更改其选定的项目。

我在Code Behind中做了这个,使用下面的代码可以正常工作:

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中的项目,并允许用户按下button来编辑选定的项目。

在代码隐藏文件中有这个逻辑可以吗? 或者,我需要通过某种绑定来做到这一点? 如果我应该通过绑定视图模型来做到这一点,任何方向将不胜感激。 谢谢。

如果只想用TextBox的文本突出显示单元格,则可以使DataGridAttatchedProperty接受来自TextBoxsearch值,并为Cell创build另一个AttatchedProperty以指示您可以使用的匹配来设置Cell风格。 然后我们创build一个IMultiValueConverter来检查Cell值是否与searchText匹配。

这样它可以在其他项目上重用,因为你只需要AttachedPropertiesConverter

AttachedProperty SearchValue绑定到您的TextBox Text属性。

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

然后为DataGridCell创build一个Style ,并使用IMultiValueConverterAttachedProperty IsTextMatch创build一个Setter,如果单元格文本与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> 

这是一个工作的例子,展示了我的ra :)声:)

码:

 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> 

结果:

在这里输入图像描述在这里输入图像描述

编辑:

如果你只想select基于单个列的行,你可以很容易地修改:)。

重写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> 

然后更改TriggerIsSelected上设置IsSelected

 <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> 

结果:

在这里输入图像描述

我现在一直在使用MVVM,而且我仍然更喜欢使用at作为指导,而不是严格的实践,部分原因是在MVVM模式中做所有事情并不总是切实可行的,如果不是,熟悉它。
我会build议只是玩弄它,直到你find一种适合你的MVVM。
如果代码与UI相关,我不相信在MVVM的代码背后有禁忌。
ScrollIntoView不是Bindable属性,所以如果你想绑定到它,你将不得不创build一个依赖属性来间接处理绑定。 至于设置选定的项目,你可以通过这样的事情做到这一点:

视图:

 <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> 

视图模型:

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