Правильный поиск DataGrid из TextBox в WPF с использованием MVVM - программирование
Подтвердить что ты не робот

Правильный поиск DataGrid из TextBox в WPF с использованием MVVM

Я новичок в шаблоне 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, который начинается с текста в текстовом поле, и позволяет пользователю нажимать кнопку для редактирования выбранного элемента.

Можно ли получить эту логику в файле "Код за файлом"? Или мне нужно сделать это через какую-то привязку? Если я буду делать это через View Model with Binding, любое направление будет оценено по достоинству. Спасибо.

4b9b3361

Ответ 1

Если вы хотите выделить ячейки с текстом из TextBox, вы можете сделать AttatchedProperty для DataGrid, чтобы принять ваше значение поиска из TextBox и создать еще один AttatchedProperty для Cell, чтобы указать совпадение, которое вы можете использовать для установки свойств в стиле Cell. Затем мы создаем IMultiValueConverter для проверки значения Cell для соответствия поиску Text.

Таким образом, он может использоваться повторно для других проектов, поскольку вам нужны только AttachedProperties и Converter

Привяжите AttachedProperty SearchValue к свойству TextBox Text.

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

Затем создайте Style для DataGridCell и создайте Setter для AttachedProperty 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>

Результат:

enter image description hereenter image description here

Изменить:

Если вы просто хотите выбрать строку на основе одного столбца, вы можете легко изменить:).

Отмените стиль 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 на 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>

Результат:

enter image description here

Ответ 2

Я использую MVVM для тишины в то время как сейчас, и я по-прежнему предпочитаю использовать в качестве ориентира, а не строгую практику, отчасти потому, что не всегда полезно делать все в шаблоне MVVM точно и даже более того, если вы не знакомы с ним.
Я бы предложил просто поиграть с ним, пока вам не удастся найти форму MVVM, которая вам подходит.

Я не верю, что это табу, чтобы иметь код в коде за MVVM, если код связан с UI.
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>

ViewModel:

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