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

时间:2013-03-17 23:27:00

标签: c# wpf search mvvm datagrid

我是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中以文本框中的文本开头的项目,并允许用户按下按钮来编辑所选项目。

在Code Behind文件中使用此逻辑是否可以?或者我需要通过某种绑定来做到这一点?如果我应该通过带有绑定的视图模型执行此操作,任何方向都将受到赞赏。谢谢。

2 个答案:

答案 0 :(得分:39)

如果您只想使用TextBox中的文字突出显示单元格,则AttatchedProperty DataGrid可以TextBox接受来自AttatchedProperty的搜索值为Cell创建另一个Cell,以指示您可以使用IMultiValueConverter样式设置属性的匹配项。然后,我们创建一个Cell来检查Text值,以便与搜索AttachedProperties匹配。

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

SearchValue TextBox绑定到您的Text <DataGrid local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}" 媒体资源。

Style

然后为DataGridCell创建AttachedProperty,并使用IsTextMatchIMultiValueConverter SearchValue创建一个Setter,以便在单元格文本与{{<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> 匹配时返回1}}

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

这是一个显示我的rambilings的工作示例:)

代码:

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

的Xaml:

DataGridRow

结果:

enter image description here enter image description here

编辑:

如果您只想根据单个列选择行,则可以非常轻松地进行修改:)。

覆盖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以设置Row

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

应该是这样的:

{{1}}

结果:

enter image description here

答案 1 :(得分:0)

我一直在使用MVVM安静一段时间,我仍然更喜欢使用at作为指导而不是严格的练习,部分是因为在MVVM模式中完全执行所有操作并不总是切实可行,更是如此你不熟悉它。
我建议你一直玩它,直到找到适合你的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>

视图模型:

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