正确的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
的文本突出显示单元格,则可以使DataGrid
的AttatchedProperty
接受来自TextBox
search值,并为Cell
创build另一个AttatchedProperty
以指示您可以使用的匹配来设置Cell
风格。 然后我们创build一个IMultiValueConverter
来检查Cell
值是否与searchText
匹配。
这样它可以在其他项目上重用,因为你只需要AttachedProperties
和Converter
将AttachedProperty
SearchValue
绑定到您的TextBox
Text
属性。
<DataGrid local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
然后为DataGridCell
创build一个Style
,并使用IMultiValueConverter
为AttachedProperty
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>
然后更改Trigger
以IsSelected
上设置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"); } }