Как мне обновить источник ContentControl
'Content
Binding?
<ContentControl Content="{Binding ViewModel.SelectedType, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={local:TypeMappingConverter}, Mode=TwoWay}" />
Я собираюсь написать приложение, в котором во время выполнения я хочу аннотировать C# -Введите и сохраните это в файл и перезагрузите его. Это скриншот моего пользовательского интерфейса:
Слева Пользователь может выбрать из доступных типов, а справа информация выбранного типа: показывается через ContentControl .
Это мой класс Model:
public class TypeMapping
{
public Type MappedType
{
get => Type.GetType(MappedTypeName);
set => MappedTypeName = value.FullName;
}
public string MappedTypeName { get; set; }
public IEnumerable<PropertyMapping> MappedProperties { get; set; } = Array.Empty<PropertyMapping>();
public virtual string SomeText { get; set; }
}
Я хочу сохранить только сопоставленные свойства, но не все доступные свойства. Так что мой TypeMapping преобразуется в TypeMappingViewModel
:
public class TypeMappingViewModel : TypeMapping, INotifyPropertyChanged
{
public IEnumerable<PropertyMapping> AvailableProperties { get; set; }
public override string SomeText
{
get => base.SomeText;
set { base.SomeText = value; NotifyPropertyChanged(); }
}
public TypeMappingViewModel(TypeMapping from)
{
MappedTypeName = from.MappedTypeName;
MappedProperties = from.MappedProperties;
AvailableProperties = MappedType.GetProperties().Select(pi => new PropertyMapping { PropertyName = pi.Name });
SomeText = from.SomeText;
}
public TypeMapping ToTypeMapping()
{
return new TypeMapping
{
MappedProperties = MappedProperties,
MappedTypeName = MappedTypeName,
SomeText = SomeText
};
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Это все другие классы:
public class MainViewModel
{
public ObservableCollection<TypeMapping> MappedTypes { get; set; }
= new ObservableCollection<TypeMapping>(new[]
{
new TypeMapping { MappedTypeName = "System.Threading.Tasks.Task" },
new TypeMapping { MappedTypeName = "System.Type" }
});
public TypeMapping SelectedType { get; set; }
}
public class PropertyMapping
{
public string PropertyName { get; set; }
public string SomeText { get; set; }
}
public partial class MainWindow : Window
{
public MainViewModel ViewModel { get; } = new MainViewModel();
public MainWindow()
{
InitializeComponent();
}
}
public class TypeMappingConverter : MarkupExtension, IValueConverter
{
#region IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TypeMapping typeMapping)
return new TypeMappingViewModel(typeMapping);
else
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TypeMappingViewModel typeMappingViewModel)
return typeMappingViewModel.ToTypeMapping();
else
return value;
}
#endregion
#region MarkupExtension
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
#endregion
}
и XAML:
<Window x:Class="ListViewHowTo.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:ListViewHowTo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="FrameworkElement" x:Key="baseStyle">
<Setter Property="Margin" Value="3" />
</Style>
</Window.Resources>
<DockPanel>
<ListBox ItemsSource="{Binding ViewModel.MappedTypes, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"
SelectedItem="{Binding ViewModel.SelectedType, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"
DisplayMemberPath="MappedTypeName"
Style="{StaticResource baseStyle}"/>
<ContentControl Content="{Binding ViewModel.SelectedType, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={local:TypeMappingConverter}, Mode=TwoWay}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:TypeMappingViewModel}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding MappedTypeName}" Style="{StaticResource baseStyle}" />
<TextBox Text="{Binding SomeText}" Style="{StaticResource baseStyle}" />
<ListView ItemsSource="{Binding AvailableProperties}" Style="{StaticResource baseStyle}">
<ListView.View>
<GridView>
<GridViewColumn Header="PropertyName" DisplayMemberBinding="{Binding PropertyName}" />
<GridViewColumn Header="SomeText" DisplayMemberBinding="{Binding SomeText}" />
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DockPanel>
</Window>
Значение правильно конвертируется. Но как мне запустить ConvertBack? В противном случае мой введенный текст будет потерян после изменения выбранного типа. Мне нужен ContentControl
, потому что позже будут разные типы сопоставления с разными видами, которые я хочу выбрать с помощью TemplateSelector
.