visual tutorial studio presentacion espaƱol ejemplos desventajas c# wpf xaml mvvm custom-controls

c# - tutorial - Problema de enlace bidireccional de objeto desconocido en propiedad de dependencia de control personalizado de WPF



xaml (1)

Tengo un control personalizado: implementado para AutoComplete TextBox. Obtuve todas las ideas de la siguiente pregunta Crea un control personalizado con la combinación de controles múltiples en WPF C # . En ese control personalizado, sugieren el siguiente código para agregar el artículo, está funcionando perfectamente y el enlace bidireccional también .

(this.ItemsSource as IList<string>).Add(this._textBox.Text);

Pero cambié el siguiente código a Objeto Desconocido, así que cambié IList<string> a IList<object>

(this.ItemsSource as IList<object>).Add(item);

XAML:

<local:BTextBox ItemsSource="{Binding Collection}" ProviderCommand="{Binding AutoBTextCommand}" AutoItemsSource="{Binding SuggCollection}" />

Pero no actualiza la colección de propiedades de ViewModel. Yo también probé los siguientes cambios en el xaml

ItemsSource="{Binding Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"

No sé dónde cometí el error.

Funcionalidad : el TextBox dentro de CustomControl toma la entrada del usuario y activa ProviderCommand , ese comando filtra los datos remotos en función de la entrada del usuario y envía la colección filtrada a través de AutoItemsSource , esta propiedad se enlaza como ItemsSource del ListBox dentro de ese CustomControl para seleccionar el artículo. Podemos seleccionar el elemento del elemento ListBox , haciendo clic en el elemento, activa Command AddCommand que está en la clase CustomControl , agrega el elemento seleccionado en ItemSource Property del CustomControl . Tengo el problema de enlace bidireccional en esta Property ItemsSource . De esta propiedad solo podemos obtener el elemento seleccionado como una Colección.

Aquí está mi código fuente completo

El código de control personalizado C #:

public class BTextBox : ItemsControl { static BTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBox), new FrameworkPropertyMetadata(typeof(BTextBox))); } #region Private Members private TextBox _textBox; private ItemsControl _itemsView; #endregion #region Dependency Property Private Members public static readonly DependencyProperty ProviderCommandProperty = DependencyProperty.Register("ProviderCommand", typeof(ICommand), typeof(BTextBox), new PropertyMetadata(null)); public static readonly DependencyProperty AutoItemsSourceProperty = DependencyProperty.Register("AutoItemsSource", typeof(IEnumerable<dynamic>), typeof(BTextBox), new PropertyMetadata(null, OnItemsSourceChanged)); #endregion #region Dependency Property Public members public IEnumerable<dynamic> AutoItemsSource { get { return (IEnumerable<dynamic>)GetValue(AutoItemsSourceProperty); } set { SetValue(AutoItemsSourceProperty, value); } } #endregion #region Listener Methods private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var tb = d as BTextBox; if ((e.NewValue != null) && ((tb.ItemsSource as IList<object>) != null)) { (tb.AutoItemsSource as IList<object>).Add(e.NewValue); } } #endregion #region Override Methods public override void OnApplyTemplate() { this._textBox = this.GetTemplateChild("PART_TextBox") as TextBox; this._itemsView = this.GetTemplateChild("PART_ListBox") as ItemsControl; this._textBox.TextChanged += (sender, args) => { if (this.ProviderCommand != null) { this.ProviderCommand.Execute(this._textBox.Text); } }; base.OnApplyTemplate(); } #endregion #region Command public ICommand ProviderCommand { get { return (ICommand)GetValue(ProviderCommandProperty); } set { SetValue(ProviderCommandProperty, value); } } public ICommand AddCommand { get { return new DelegatingCommand((obj) => { (this.ItemsSource as IList<object>).Add(obj); }); } } #endregion }

El código Generic.xaml es

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SampleControl"> <Style TargetType="{x:Type local:BTextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:BTextBox}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="40"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBox x:Name="PART_TextBox" Grid.Row="0" Width="*" VerticalAlignment="Center" /> <ListBox ItemsSource="{TemplateBinding AutoItemsSource}" Grid.Row="1" x:Name="PART_ListBox_Sugg" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <CheckBox IsChecked="{Binding Value.IsChecked}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:BTextBox}}, Path=AddCommand}" CommandParameter="{Binding }" Foreground="#404040"> <CheckBox.Content> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding }" Visibility="Visible" TextWrapping="Wrap" MaxWidth="270"/> </StackPanel> </CheckBox.Content> </CheckBox> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>

El código MainWindow.xaml es

<Window x:Class="SampleControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SampleControl" Title="MainWindow" Height="400" Width="525"> <Grid> <local:BTextBox ItemsSource="{Binding Collection}" ProviderCommand="{Binding AutoBTextCommand}" AutoItemsSource="{Binding SuggCollection}" /> </Grid> </Window>

Código de MainWindow.xaml detrás del código de C #

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new StringModel(); } }

Estoy teniendo DOS ViewModels

ViewModel # 1 StringModel

class StringModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection<string> _collection = new ObservableCollection<string>(); private ObservableCollection<string> _suggCollection = new ObservableCollection<string>(); private ObservableCollection<string> _primaryCollection = new ObservableCollection<string>(); public ObservableCollection<string> Collection { get { return _collection; } set { _collection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection")); } } public ObservableCollection<string> SuggCollection { get { return _suggCollection; } set { _suggCollection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection")); } } public StringModel() { _primaryCollection = new ObservableCollection<string> { "John", "Jack", "James", "Emma", "Peter" }; } public ICommand AutoBTextCommand { get { return new DelegatingCommand((obj) => { Search(obj as string); }); } } private void Search(string str) { SuggCollection = new ObservableCollection<string>(_primaryCollection.Where(m => m.ToLowerInvariant().Contains(str.ToLowerInvariant())).Select(m => m)); } }

ViewModel # 2 IntModel

class IntModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection<int> _collection = new ObservableCollection<int>(); private ObservableCollection<int> _suggCollection = new ObservableCollection<int>(); private ObservableCollection<int> _primaryCollection = new ObservableCollection<int>(); public ObservableCollection<int> Collection { get { return _collection; } set { _collection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Collection")); } } public ObservableCollection<int> SuggCollection { get { return _suggCollection; } set { _suggCollection = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SuggCollection")); } } public IntModel() { _primaryCollection = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20 }; } public ICommand AutoBTextCommand { get { return new DelegatingCommand((obj) => { Search(obj as string); }); } } private void Search(string str) { int item = 0; int.TryParse(str, out item); SuggCollection = new ObservableCollection<int>(_primaryCollection.Where(m => m == item).Select(m => m)); } }


En primer lugar, esta publicación se habría ajustado mejor en CodeReview.

En segundo lugar, me puedo imaginar lo que querías hacer. Para acortar las cosas, le recomiendo que no use colecciones genéricas en su caso.

Modifiqué un poco el Control:

public class BTextBox : ItemsControl { static BTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBox), new FrameworkPropertyMetadata(typeof(BTextBox))); } private TextBox _textBox; private ItemsControl _itemsView; public static readonly DependencyProperty ProviderCommandProperty = DependencyProperty.Register("ProviderCommand", typeof(ICommand), typeof(BTextBox), new PropertyMetadata(null)); public static readonly DependencyProperty AutoItemsSourceProperty = DependencyProperty.Register("AutoItemsSource", typeof(IEnumerable), typeof(BTextBox), new PropertyMetadata(null, OnItemsSourceChanged)); public IEnumerable AutoItemsSource { get { return (IEnumerable)GetValue(AutoItemsSourceProperty); } set { SetValue(AutoItemsSourceProperty, value); } } private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var tb = d as BTextBox; if ((e.NewValue != null) && ((tb.ItemsSource as IList) != null)) { foreach (var item in e.NewValue as IEnumerable) { (tb.AutoItemsSource as IList).Add(item); } } } public override void OnApplyTemplate() { this._textBox = this.GetTemplateChild("PART_TextBox") as TextBox; this._itemsView = this.GetTemplateChild("PART_ListBox_Sugg") as ItemsControl; this._itemsView.ItemsSource = this.AutoItemsSource; this._textBox.TextChanged += (sender, args) => { this.ProviderCommand?.Execute(this._textBox.Text); }; base.OnApplyTemplate(); } public ICommand ProviderCommand { get { return (ICommand) this.GetValue(ProviderCommandProperty); } set { this.SetValue(ProviderCommandProperty, value); } } public ICommand AddCommand { get { return new RelayCommand(obj => { (this.ItemsSource as IList)?.Add(obj); }); } } }

Luego he arreglado tu XAML para que incluso compile y ejecute:

<Style TargetType="{x:Type local:BTextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:BTextBox}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="40"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBox x:Name="PART_TextBox" Grid.Row="0" VerticalAlignment="Center" /> <ListBox ItemsSource="{TemplateBinding AutoItemsSource}" Grid.Row="1" x:Name="PART_ListBox_Sugg" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <CheckBox Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:BTextBox}}, Path=AddCommand}" CommandParameter="{Binding}" Foreground="#404040"> <CheckBox.Content> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding }" Visibility="Visible" TextWrapping="Wrap" MaxWidth="270"/> </StackPanel> </CheckBox.Content> </CheckBox> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>

Por último, una valiosa observación:

Nunca permita setters en sus ItemsSources. Si los reemplaza, la unión se romperá. Utilice .Clear() y .Add() lugar como se ve a continuación:

public class StringModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private readonly ObservableCollection<string> _collection = new ObservableCollection<string>(); private readonly ObservableCollection<string> _suggCollection = new ObservableCollection<string>(); private readonly ObservableCollection<string> _primaryCollection = new ObservableCollection<string>(); public ObservableCollection<string> Collection => this._collection; public ObservableCollection<string> SuggCollection => this._suggCollection; public StringModel() { this._primaryCollection.Add("John"); this._primaryCollection.Add("Jack"); this._primaryCollection.Add("James"); this._primaryCollection.Add("Emma"); this._primaryCollection.Add("Peter"); } public ICommand AutoBTextCommand { get { return new RelayCommand(obj => { this.Search(obj as string); }); } } private void Search(string str) { this.SuggCollection.Clear(); foreach (var result in this._primaryCollection.Where(m => m.ToLowerInvariant().Contains(str.ToLowerInvariant())).Select(m => m)) { this.SuggCollection.Add(result); } } }

Nota

Sice que no tenía su delegación DelegateCommand , he utilizado mi RelayCommand en RelayCommand lugar. Puedes cambiarlo sin problemas. Creo que es lo mismo pero tiene un nombre diferente.
También podría considerar mostrar sus sugerencias desde el principio. Esto podría proporcionar una mejor experiencia de usuario, pero eso es solo mi opinión