c# - two - Búsqueda adecuada de DataGrid desde TextBox en WPF usando MVVM
updatesourcetrigger wpf (2)
Soy nuevo en el patrón MVVM, y estoy un poco confundido sobre cuándo usar Code Behind. Tengo un formulario muy simple en este momento, que incluye un TextBox y un DataGrid. Lo que me gustaría es poder hacer que DataGrid cambie su elemento seleccionado en función del TextBox.
He hecho esto en Code Behind y funciona bien usando el siguiente código:
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 { }
}
}
Ahora, solo quiero resaltar el elemento en el cuadro de datos que comienza con el texto en el cuadro de texto y permitir que el usuario presione un botón para editar el elemento seleccionado.
¿Está bien tener esta lógica en el archivo Código Detrás? ¿O tendría que hacer esto a través de algún tipo de enlace? Si debo hacer esto a través del Modelo de Vista con Encuadernación, cualquier dirección sería apreciada. Gracias.
He estado usando MVVM por un momento de calma, y sigo prefiriendo usar como una guía en lugar de una práctica estricta, en parte porque no siempre es práctico hacer todo en el patrón de MVVM exactamente, y más aún si no estás familiarizarse con ella.
Yo sugeriría simplemente jugar con él hasta que logre encontrar una forma de MVVM que se adapte a usted.
No creo que sea un tabú tener Código en el Código Detrás de la MVVM si el código está relacionado con la IU.
ScrollIntoView no es una propiedad vinculable, por lo que si desea vincularla, tendrá que crear una propiedad de dependencia para manejar indirectamente la vinculación. En cuanto a la configuración del elemento seleccionado, puede hacerlo a través de algo como:
Ver:
<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");
}
}
Si solo desea resaltar las celdas con el texto del TextBox
, puede hacer una AttatchedProperty
de AttatchedProperty
para que el DataGrid
acepte su valor de búsqueda del TextBox
y crear otra AttatchedProperty
de AttatchedProperty
para la Cell
para indicar una coincidencia que puede usar para establecer propiedades en el Estilo de Cell
. Luego creamos un IMultiValueConverter
para verificar el valor de Cell
para una coincidencia con el Text
búsqueda.
De esta manera, puede reutilizarse en otros proyectos, ya que solo necesita las AttachedProperties
y el Converter
Enlace el SearchValue
de SearchValue
AttachedProperty
a su propiedad TextBox
Text
.
<DataGrid local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
Luego cree un Style
para DataGridCell
y cree un Setter para AttachedProperty
IsTextMatch
utilizando el IMultiValueConverter
para devolver si el texto de las celdas coincide con el valor 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>
Luego podemos usar la propiedad IsTextMatch
Cells
adjuntas para establecer un resaltado usando un Trigger
<Style.Triggers>
<Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
<Setter Property="Background" Value="Orange" />
</Trigger>
</Style.Triggers>
Aquí hay un ejemplo de trabajo que muestra mis rambilings :)
Código:
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>
Resultado:
Editar:
Si solo desea seleccionar la fila basada en una sola columna, puede modificarla muy fácilmente :).
Reemplace el estilo de DataGridRow
lugar de DataGridCell
.
<Style TargetType="{x:Type DataGridRow}">
Primero pase la propiedad que desea al IMultiValueConverter
este debe ser su DataContext
<MultiBinding Converter="{StaticResource SearchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="DataContext.MyProperty" />
<Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
</MultiBinding>
Luego cambie el Trigger
para establecer IsSelected
en la Row
<Style.Triggers>
<Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
Debería verse así:
<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>
Resultado: