2016-11-10 1 views
1

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)

+0

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

+0

Ich habe alle "Sanitär" Code hinzugefügt. – fharreau

+0

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

Antwort

0

ich endlich herausfinden:

Die Items Abhängigkeitseigenschaft von ParameterListControl war ein IList<string>. Es war ein Fehler beim Kopieren/Einfügen von einem anderen UC. Habe ich es zu IEnumerable und alles funktioniert jetzt:

public IEnumerable Items 
{ 
    get { return (IEnumerable)GetValue(ItemsProperty); } 
    set { SetValue(ItemsProperty, value); } 
} 

public static readonly DependencyProperty ItemsProperty = 
     DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ParameterListControl), new PropertyMetadata(new List<object>())); 

Ich fuhr fort, auf dem Code zu arbeiten, und es ist jetzt fertig und wirklich Composite im Vergleich zu der Probe ich bereits geschrieben. Wenn jemand diesen Code sehen/verwenden möchte, finden Sie ihn unter github.

+0

froh, dass Sie es herausgefunden haben :) – Dbl

0

ich Sie sehen eine Bindung MainViewModel.Parameters haben -> ParameterListControl.Items aber Sie könnten die Bindung von ParameterListControl.Items fehlt -> Parameterlist. Parameter. (Das ist vorausgesetzt Parameterlist das Ansichtsmodell für die ParameterListControl ist -. Sie bieten den Code für Datacontext-Bindungen)

the accepted answer on this question See. (Ignorieren Sie den Kommentar zu Caliburn.Micro - die gleiche Lösung funktionierte für mich in MVVM Light.)

Im Wesentlichen erstellen Sie im Konstruktor von ParameterListControl eine zusätzliche Bindung zwischen der Abhängigkeitseigenschaft der Ansicht und der Eigenschaft des Ansichtsmodells.

(Auch ist Dbl rechts in den Kommentaren, dass bei Bindung Probleme debuggen, die „unwichtige“ „Sanitär“ Code, den Sie sehr wichtig ist weggelassen.)

+0

Ich bin mir nicht sicher, deinen ersten Satz zu verstehen. Ich habe jetzt den ganzen Klempnercode hinzugefügt. Können Sie sehen, ob Ihre Hypothese noch gültig ist? Was ich nicht verstehe ist, warum VM.Name korrekt an UC.ParamName gebunden ist, wenn VM.Paramters nicht an UC.Items gebunden ist. Ich kann nicht sehen, wo die zwei Bindungen differents sind. Ich kann nicht glauben, dass ich die Bindung manuell im Code hinter nur für einige Eigenschaften und nicht für andere registrieren muss ... – fharreau

Verwandte Themen