Zunächst einmal bin ich ein WPF Anfänger! Mein Ansatz ist möglicherweise nicht der richtige Weg, um das zu tun, was ich will, also zögere nicht, mir zu sagen, ob das der Fall ist. Was ich tun möchte, ist ein zusammengesetztes Benutzersteuerelement in WPF, das MVVM verwendet.WPF benutzerdefinierte zusammengesetzte Benutzersteuerelemente
Einige Klassen werden eine bessere Vorstellung als ich, hier sind meine Ansicht Modelle:
interface IParameter : INotifyPropertyChanged
{
string Name { get; set;}
string Value { get; set;}
}
class TextParameter : ViewModelBase, IParameter
{
private string _value;
public string Name { get; private set; }
public string Value
{
get { return _value; }
set
{
_value = value;
RaisePropertyChanged();
}
}
public TextParameter (string name)
{
this.Name = name;
}
}
class ParameterList : ViewModelBase, IParameter
{
private string _value;
public string Name { get; private set; }
public string Value
{
get { return _value; }
set
{
_value = value;
RaisePropertyChanged();
}
}
ObservableCollection<IParameter> Parameters { get; set; }
public ParameterList (string name, IEnumerable<IParameter> parameters = null)
{
this.Name = name;
this.Parameters = new ObservableCollection<IParameter>(parameters ?? new List<IParameter>());
}
}
I MVVM Licht verwende, so das alle Property Sachen in ViewModelBase verwaltet. Auch ist dies keine erschöpfende Liste aller Parameter, es gibt einige andere, komplexer, aber das Problem ist über diese.
Hier sind meine benutzerdefinierte Kontrollen:
TextParameterControl.xaml:
<UserControl x:Class="Stuff.TextParameterControl" [..] x:Name="parent">
<StackPanel DataContext="{Binding ElementName=parent}" Orientation="Horizontal">
<TextBlock Text="{Binding Path=ParamName, StringFormat='{}{0}:'}" Width="100"></TextBlock>
<TextBox Text="{Binding Path=Value}" Width="100"></TextBox>
</StackPanel>
</UserControl>
TextParameterControl.xaml.cs:
public class TextParameterControl : UserControl
{
#region param name
public string ParamName
{
get { return (string)GetValue(ParamNameProperty); }
set { SetValue(ParamNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParamNameProperty =
DependencyProperty.Register("ParamName", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty));
#endregion
#region value
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(TextParameterControl), new PropertyMetadata(String.Empty));
#endregion
public TextParameterControl()
{
InitializeComponent();
}
}
ParameterListControl.xaml:
<UserControl x:Class="Stuff.ParameterListControl" [..] x:Name="parent">
<UserControl.Resources>
<DataTemplate x:Key="TextParameterTemplate">
<c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="ParameterListTemplate">
<c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" />
</DataTemplate>
<s:ParameterTemplateSelector x:Key="ParameterSelector"
TextParameterTemplate="{StaticResource TextParameterTemplate}"
ParameterListTemplate="{StaticResource ParameterListTemplate}"/>
</UserControl.Resources>
<Expander DataContext="{Binding ElementName=parent}" Header="{Binding Path=ParamName}" IsExpanded="True" ExpandDirection="Down">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=Items}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl>
</StackPanel>
</Expander>
</UserControl>
ParameterListControl.xaml.cs:
public partial class ParameterListControl: UserControl
{
#region param name
public string ParamName
{
get { return (string)GetValue(ParamNameProperty); }
set { SetValue(ParamNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ParamName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParamNameProperty =
DependencyProperty.Register("ParamName", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty));
#endregion
#region value
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(ParameterListControl), new PropertyMetadata(String.Empty));
#endregion
#region items
public IList<string> Items
{
get { return (List<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IList<string>), typeof(ParameterListControl), new PropertyMetadata(new List<string>()));
#endregion
public ParameterListControl()
{
InitializeComponent();
}
}
Hier ist mein eigenes Template Wähler:
class ParameterTemplateSelector : DataTemplateSelector
{
public DataTemplate ParameterListTemplate { get; set; }
public DataTemplate TextParameterTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is TextParameter)
{
return this.TextParameterTemplate;
}
else if (item is ParameterList)
{
return this.ParameterListTemplate;
}
throw new Exception(String.Format("This parameter ({0}) is not handled in the application", item.GetType().Name));
}
}
Und hier ist der Aufruf Ansicht und Ansichtsmodell:
Ansichtsmodell:
public class MainViewModel : ViewModelBase
{
public ObservableCollection<IParameter> Parameters { get; set; }
public MainViewModel()
{
this.Parameters = new ObservableCollection<IParameter>();
this.Parameters.Add(new TextParameter("Customer"));
// here I am building my complex composite parameter list
}
anzeigen :
<UserControl.Resources>
<DataTemplate x:Key="TextParameterTemplate">
<c:TextParameterControl ParamName="{Binding Name}" Value="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="ParameterListTemplate">
<c:ParameterListControl ParamName="{Binding Name}" Value="{Binding Value}" Items="{Binding Parameters}" />
</DataTemplate>
<s:ParameterTemplateSelector x:Key="ParameterSelector"
TextParameterTemplate="{StaticResource TextParameterTemplate}"
ParameterListTemplate="{StaticResource ParameterListTemplate}"/>
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding Parameters}" ItemTemplateSelector="{StaticResource ParameterSelector}"></ItemsControl>
Wenn ich die Anwendung ausführen, sind die TextParameter
im MainViewModel.Parameters
gut geladen (VM.Name
und VM.Value
Eigenschaften sind UC.ParamName
und UC.Value
gut binded. Umgekehrt sind die ParameterList
in MainViewModel.Parameters
teilweise geladen. UC.Name
ist die UC.ParamName
binded aber die VM.Parameters
wird binded nicht auf die UC.Items
(die UC.DataContext
wird die VM wird die VM.Parameters
gut definiert, aber die UC.Items
ist dringend null
).
Haben Sie eine Idee von dem, was ich vermisse? (ich bin kein Muttersprachler, entschuldigen Sie mich, wenn mein Englisch weh tut)
Sie haben verbindliche Probleme und schließen den inotifypropertychanged-Code aus? sehr schlechte Idee. Auch Parameter zeigt keinen eigenschaftsveränderten Code/Initialisierer. auch schlechte Idee – Dbl
Ich habe alle "Sanitär" Code hinzugefügt. – fharreau
für mich sieht es aus wie das Problem ist immer noch, dass Ihre Ansicht versucht, an eine Eigenschaft (Parameter) zu binden, bevor es gesetzt ist, und da Sie keine INotifyPropertyChanged-Aufrufe auf Ihre Parameter-Eigenschaft, wird WPF nicht bemerken und Ihre Bindung kann nicht aufgelöst werden – Dbl