【发布时间】:2016-09-27 11:37:05
【问题描述】:
我正在尝试构建一个允许编辑列表中项目的用户控件。 首先,我将向您展示我拥有的代码,并在代码下方解释我的问题。如果您想重现该问题,您可以创建一个新项目并将所有代码复制到其中。我将包括所有需要的课程。
用户控件将绑定到的列表的代码:
using System.Collections.Generic;
namespace TestApplicationWPF
{
public class SettingList : List<ISetting>
{
}
}
ISetting接口:
namespace TestApplicationWPF
{
public class ISetting
{
string Name { get; set; }
}
}
以及我写入列表的通用对象:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TestApplicationWPF
{
public class Setting<T> : ISetting, INotifyPropertyChanged
{
public string Name { get; set; }
private T value;
public T Value
{
get
{
return value;
}
set
{
this.value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
现在用户控件的目标是绑定到 SettingList 的实例,并允许用户编辑列表中每个“设置”实例的“值”属性。为此,用户控件根据设置中的 T 类型显示特定控件。例如,字符串显示在文本框中,日期时间值将显示在 DatePicker 中。
用户控件的代码如下所示:
<UserControl x:Class="TestApplicationWPF.SettingEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestApplicationWPF"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<local:TypeOfConverter x:Key="TypeOfConverter" />
<Style x:Key="TypedValueStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Width" Value="200" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Path=.}" IsReadOnly="true" Background="LightGray"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource TypeOfConverter},Path=Value}"
Value="String">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<ListView Name="LvPluginSettings"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SettingEditor}}, Path=Settings, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="0">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Value"
Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TypedValueStyle}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Button Name ="BtnSetValue" Grid.Row="1" Content="Set the value" Click="BtnSetValue_Click"/>
<Button Name ="BtnGetValue" Grid.Row="2" Content="What's the value?" Click="BtnGetValue_Click"/>
</Grid>
</UserControl>
用户控件的 .cs 代码:
using System.Windows;
using System.Windows.Controls;
namespace TestApplicationWPF
{
public partial class SettingEditor : UserControl
{
public SettingList Settings
{
get { return (SettingList)GetValue(PluginSettingsProperty); }
set { SetValue(PluginSettingsProperty, value); }
}
public static readonly DependencyProperty PluginSettingsProperty = DependencyProperty.Register(
"Settings",
typeof(SettingList),
typeof(SettingEditor),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public SettingEditor()
{
InitializeComponent();
}
private void BtnSetValue_Click(object sender, RoutedEventArgs e)
{
((Setting<string>)Settings[0]).Value = "Different value now.";
}
private void BtnGetValue_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(((Setting<string>)Settings[0]).Value);
}
}
}
为了确定列表中每个项目的“值”属性的类型,我使用了“TypeOfConverter”。转换器如下所示:
using System;
using System.Windows.Data;
namespace TestApplicationWPF
{
public class TypeOfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value == null) ? null : value.GetType().Name;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
最后,为了能够完全重现问题,我为您提供了使用用户控件的 MainWindow,它是 ViewModel: 窗口:
<Window x:Class="TestApplicationWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestApplicationWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MainWindowViewModel x:Key="MainWindowViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource MainWindowViewModel}">
<local:SettingEditor Settings="{Binding Path=Settings}" />
</Grid>
</Window>
视图模型:
namespace TestApplicationWPF
{
public class MainWindowViewModel
{
public SettingList Settings { get; set; }
public MainWindowViewModel()
{
Settings = new SettingList();
Settings.Add(new Setting<string> { Name="Name of setting", Value = "HelloWorld" });
}
}
}
我的问题是设置实例的值属性的绑定。当我启动应用程序时,它会向我显示非常好的值。我得到一个带有“HelloWorld”的文本框。此外,当背景中的值更改时,它将更新到文本框。 但是,当我将光标设置到文本框中,将文本更改为其他内容并离开文本框时,它不会在绑定的“值”-属性中更改。此外,在我尝试编辑文本框中的文本后,在后台所做的更改不再影响文本框。
如果有人能帮助我解决这个问题,我将不胜感激。即使是对可能出错的最小提示也会对我有很大帮助。
您好, 斯文
【问题讨论】:
标签: c# wpf generics data-binding user-controls