20

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

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

4

2 回答 2

49

如果您只想使用来自 的文本突出显示单元格,您TextBox可以AttatchedPropertyDataGrid接受您的搜索值TextBoxAttatchedProperty为 然后我们创建一个来检查与 search 匹配的值。CellCellIMultiValueConverterCellText

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

绑定AttachedProperty SearchValue到您的TextBox Text财产。

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

如果单元格文本StyleDataGridCellAttachedProperty IsTextMatchIMultiValueConverterSearchValue

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

xml:

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

首先将您想要的属性传入IMultiValueConverterthis 应该是您的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>

结果:

在此处输入图像描述

于 2013-03-18T02:48:26.293 回答
1

我已经使用 MVVM 很长一段时间了,我仍然更喜欢将它用作指导而不是严格的实践,部分原因是完全按照 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");
      }
 }
于 2013-03-18T02:01:54.780 回答