Я пытаюсь создать повторно используемый элемент управления, который может отображать список введенных значений и возможность удаления значений. Способ, которым он представлен, должен основываться на «DisplayMemberPath» или быть шаблоном. Это близко к тому, чего я пытаюсь достичь.Пользовательский UserControl с templating
[! [Вдохновение] [1]] [1]
До сих пор я создал собственный UserControl
<UserControl x:Class="MyNamespace.CriteriaView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl x:Name="ItemsControl"
Grid.Row="0"
Padding="2,2,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray"
BorderThickness="0.6"
Margin="0,0,2,2">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding .}"
Padding="0"
Margin="1" />
<Button Click="ButtonBase_OnClick"
Margin="1">
<Button.Template>
<ControlTemplate>
<Image Source="{DynamicResource RemoveIcon}" />
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox x:Name="TextBlock"
Grid.Row="1"
PreviewKeyDown="UIElement_OnPreviewKeyDown"/>
</Grid>
</UserControl>
код за
using System;
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace MyNamespace
{
/// <summary>
/// Interaction logic for CriteriaView.xaml
/// </summary>
public partial class CriteriaView : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource",
typeof (IEnumerable),
typeof (CriteriaView),
new PropertyMetadata(default(IEnumerable)));
public IEnumerable ItemsSource {
get { return (IEnumerable)this.GetValue(ItemsSourceProperty); }
set { this.SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register(
"DisplayMemberPath",
typeof (string),
typeof (CriteriaView),
new PropertyMetadata(default(string)));
public string DisplayMemberPath {
get { return (string)GetValue(DisplayMemberPathProperty); }
set { SetValue(DisplayMemberPathProperty, value); }
}
public static readonly DependencyProperty AddCommandProperty = DependencyProperty.Register(
"AddCommand",
typeof (ICommand),
typeof (CriteriaView),
new PropertyMetadata(default(ICommand)));
public ICommand AddCommand {
get { return (ICommand)GetValue(AddCommandProperty); }
set { SetValue(AddCommandProperty, value); }
}
public static readonly DependencyProperty RemoveCommandProperty = DependencyProperty.Register(
"RemoveCommand",
typeof (ICommand),
typeof (CriteriaView),
new PropertyMetadata(default(ICommand)));
public ICommand RemoveCommand {
get { return (ICommand)GetValue(RemoveCommandProperty); }
set { SetValue(RemoveCommandProperty, value); }
}
public CriteriaView()
{
this.InitializeComponent();
}
public override void OnApplyTemplate() {
base.OnApplyTemplate();
this.ItemsControl.ItemsSource = this.ItemsSource;
}
private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e) {
if (this.AddCommand != null && (e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Tab))
this.AddCommand.Execute(this.TextBlock.Text);
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e) {
if (RemoveCommand == null) return;
var no = (sender as FrameworkElement)?.DataContext as int?;
RemoveCommand.Execute(no);
}
}
}
И желаемого использования
<view:CriteriaView ItemsSource="{Binding Path=Criteria.SerialNumbers}"
AddCommand="{Binding AddCommand}"
RemoveCommand="{Binding RemoveCommand}"
DisplayMemberPath="PropertyName"/>
И у пользователя UserCo ntrol обрабатывает остальные. Там, где я отстаю, мы включаем «DisplayMemberPath» в шаблон, который может использовать ItemsControl.
Любые подсказки о том, как это сделать?
/Edit: Решение
Я создал пользовательский элемент управления, как это:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace MyNamespace.Views
{
public class CriteriaView : ItemsControl
{
static CriteriaView()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CriteriaView),
new FrameworkPropertyMetadata(typeof(CriteriaView)));
}
public static readonly DependencyProperty AddCommandProperty = DependencyProperty.Register(
"AddCommand",
typeof(ICommand),
typeof(CriteriaView),
new PropertyMetadata(default(ICommand)));
public ICommand AddCommand
{
get { return (ICommand)GetValue(AddCommandProperty); }
set { SetValue(AddCommandProperty, value); }
}
public static readonly DependencyProperty RemoveCommandProperty = DependencyProperty.Register(
"RemoveCommand",
typeof(ICommand),
typeof(CriteriaView),
new PropertyMetadata(default(ICommand)));
public ICommand RemoveCommand
{
get { return (ICommand)GetValue(RemoveCommandProperty); }
set { SetValue(RemoveCommandProperty, value); }
}
private TextBox _box;
public override void OnApplyTemplate()
{
this._box = this.GetTemplateChild("PART_AddElementTextBox") as TextBox;
if (this._box != null) this._box.PreviewKeyDown += this.UIElement_OnPreviewKeyDown;
base.OnApplyTemplate();
}
private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (this.AddCommand != null
&& (e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Tab)) {
var preCount = this.ItemsSource.Cast<object>().Count();
this.AddCommand.Execute(this._box.Text);
var postCount = this.ItemsSource.Cast<object>().Count();
if (postCount != preCount) this._box.Text = String.Empty;
}
}
}
}
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:MyNamespace.Views">
<views:DisplayMemberPathConverter x:Key="DisplayMemberPathConverter" />
<Style TargetType="{x:Type views:CriteriaView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:CriteriaView}">
<Border BorderBrush="Gray"
BorderThickness="0.6"
Margin="0,0,2,2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0"
Padding="2,2,0,0"
ItemsSource="{TemplateBinding ItemsSource}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray"
BorderThickness="0.6"
Margin="0,0,2,2">
<StackPanel Orientation="Horizontal">
<Label Padding="0"
Margin="1">
<Label.Content>
<MultiBinding Converter="{StaticResource DisplayMemberPathConverter}">
<Binding Path="."/>
<Binding Path="DisplayMemberPath"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=views:CriteriaView}"/>
</MultiBinding>
</Label.Content>
</Label>
<Button Margin="1"
Command="{Binding RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=views:CriteriaView}}"
CommandParameter="{Binding .}">
<Button.Template>
<ControlTemplate>
<Image Source="{DynamicResource RemoveIcon}" />
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox x:Name="PART_AddElementTextBox"
Grid.Row="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
и преобразователь
using System;
using System.Globalization;
using System.Windows.Data;
namespace MyNamespace.Views
{
public class DisplayMemberPathConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2) return null;
var prop = values[1] as string;
var obj = values[0];
if (prop == null || obj == null) return obj;
var result = obj.GetType().GetProperty(prop)?.GetValue(obj, null);
return result ?? obj;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}
}
И использование это strai GHT вперед
<view:CriteriaView ItemsSource="{Binding Path=Criteria.SerialNumbers}"
AddCommand="{Binding AddSerial}"
RemoveCommand="{Binding RemoveSerial}"
DisplayMemberPath="." />
Я рекомендую сделать CustomControl, полученный из ItemsControl вместо UserControl. Вы можете использовать эту тему, плюс вы можете использовать {TemplateBinding} – lokusking
. Я действительно занимался этим, но я не мог найти многого, добавляя к нему текстовое поле. У вас есть фрагменты кода или лучше, некоторые документы. То, что я могу найти в MSDN, не показалось мне полезным. – hvidgaard