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

Posted

技术标签:

【中文标题】使用 MVVM 从 WPF 中的 TextBox 进行正确的 DataGrid 搜索【英文标题】:Proper DataGrid search from TextBox in WPF using MVVM 【发布时间】:2013-03-06 05:48:48 【问题描述】:

我是 MVVM 模式的新手,对何时使用 Code Behind 有点困惑。我现在有一个非常简单的表单,其中包括一个 TextBox 和一个 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 中以文本框中的文本开头的项目,并允许用户按下按钮来编辑所选项目。

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

【问题讨论】:

【参考方案1】:

如果您只想用TextBox 中的文本突出显示单元格,您可以为DataGrid 创建一个AttatchedProperty,以接受来自TextBox 的搜索值,并为@ 创建另一个AttatchedProperty 987654329@ 表示可用于设置Cell 样式中的属性的匹配项。然后我们创建一个IMultiValueConverter 来检查Cell 值是否与搜索Text 匹配。

这种方式可以在其他项目中重复使用,因为您只需要 AttachedPropertiesConverter

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

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

然后为DataGridCell 创建一个Style 并为AttachedProperty 创建一个Setter 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>

结果:

编辑:

如果您只想基于单个列选择行,您可以很容易地修改:)。

覆盖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更改为在Row上设置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>

结果:

【讨论】:

哇,感谢您为此所做的所有工作。老实说,我要做的就是根据第一列中的文本选择整行。我可以为此调整您的代码吗? 我添加了一个编辑,显示如何修改以做你需要的,它很容易改变:) @sa_ddam213 感谢您的回答。这就是我喜欢这个网站的原因。我们有很多事情可以尝试。 @sa_ddam213 是否可以将搜索到的项目滚动到视图中 我不能在这里回答,但如果你是一个新问题,我可以告诉你如何:)【参考方案2】:

我使用 MVVM 已经有一段时间了,我仍然更喜欢将其用作指导而不是严格的实践,部分原因是完全按照 MVVM 模式做所有事情并不总是可行的,如果您对它不太熟悉。我建议您只是玩弄它,直到您设法找到适合您的 MVVM 形式。 如果代码与 UI 相关,我不认为在 MVVM 的代码中包含代码是禁忌。 ScrollIntoView 不是可绑定属性,因此如果要绑定到它,则必须创建依赖项间接处理绑定的属性。至于设置所选项目,您可以通过以下方式完成:

查看:

<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");
      
 

【讨论】:

以上是关于使用 MVVM 从 WPF 中的 TextBox 进行正确的 DataGrid 搜索的主要内容,如果未能解决你的问题,请参考以下文章

WPF MVVM - 如何绑定自定义控件->ToggleButton.IsChecked 到 View->TextBox.Text

MVVM DEVDataColumn中的TextBox与ComboBox的并存

WPF MVVM:如何根据事件更新 UI 控制器

WPF MVVM 架构 Step By Step(把actions从view model解耦)

从WPF中的View和View Model访问属性

WPF MVVM模式,有两个ListBox和一个TxtBox,选任一个ListBox的Item ,就显示在TextBox上。请帮帮高手。。