Blog
Von überall im XAML auf ModelView zugreifen mittels Attached Property
Angenommen wir machen für ein Control ein DataTemplate vom Typ Artikel und möchten darin angeben wie viel Artikel es insgesamt gibt.
Dann stehen wir vor dem Problem, dass innerhalb vom DataTemplate der DataContext auf den Typ Artikel beschränkt ist. Wir können nicht auf übergeordnete Elemente zugreifen, welche z.B. den Artikel selber beinhalten.
Die Lösung für dieses Problem kann eine Attached Property sein. Die Attached Property gibt in unserem Beispiel den aktuellen DataContext des ViewModels zurück.
Mit der Attached Property können wir somit, von jeder "Position" aus, auch wenn wir im Datenmodel uns hierarchisch ganz unten befinden, auf übergeordnete Elemente zugreifen.
Ich hab dazu ein Beispiel erstellt.
Zuerst das Datenmodel
public class Artikel
{
public ArtikelDetails Artikeldetails { get; set; }
public string Foto { get; set; }
}
public class ArtikelDetails
{
public int ArtikelNr { get; set; }
public string Artikelname { get; set; }
public bool Vorrat { get; set; }
}
{
public ArtikelDetails Artikeldetails { get; set; }
public string Foto { get; set; }
}
public class ArtikelDetails
{
public int ArtikelNr { get; set; }
public string Artikelname { get; set; }
public bool Vorrat { get; set; }
}
Im ModelView (in diesem Beispiel Facade) befindet sich die Attached Property. Als Typ wird die Facade angegeben. Die Attached Propertie wird dann immer unsere aktuelle Instanz der Facade zurück geben.
public class ArtikelFacade : INotifyPropertyChanged
{
public ArtikelFacade()
{
ArtikelList = new ObservableCollection<Artikel>();
ArtikelList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ArtikelList_CollectionChanged);
}
void ArtikelList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("AnzahlArtikel");
}
public int AnzahlArtikel
{
get
{
return ArtikelList.Count;
}
set
{
NotifyPropertyChanged("AnzahlArtikel");
}
}
/// <summary>
/// In der ArtikelList speichern wir unsere Waren ab. Als Typ könnte man auch IList angeben
/// </summary>
public ObservableCollection<Artikel> ArtikelList { get; set; }
#region ArtikelFacade AttachedProperty
public static ArtikelFacade GetFacadeRootParent(DependencyObject obj)
{
return (ArtikelFacade)obj.GetValue(FacadeRootParentProperty);
}
public static void SetFacadeRootParent(DependencyObject obj, ArtikelFacade value)
{
obj.SetValue(FacadeRootParentProperty, value);
}
/// <summary>
/// Die Attached Property gibt die aktuelle Instanz von ArtikelFacade zurück. Damit können wir im XAML von jeder bleliebigen Stelle aus, auf die Facade zugreifen.
/// </summary>
public static readonly DependencyProperty FacadeRootParentProperty =
DependencyProperty.RegisterAttached("FacadeRootParent", typeof(ArtikelFacade), typeof(ArtikelFacade), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
#endregion
{
public ArtikelFacade()
{
ArtikelList = new ObservableCollection<Artikel>();
ArtikelList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ArtikelList_CollectionChanged);
}
void ArtikelList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("AnzahlArtikel");
}
public int AnzahlArtikel
{
get
{
return ArtikelList.Count;
}
set
{
NotifyPropertyChanged("AnzahlArtikel");
}
}
/// <summary>
/// In der ArtikelList speichern wir unsere Waren ab. Als Typ könnte man auch IList angeben
/// </summary>
public ObservableCollection<Artikel> ArtikelList { get; set; }
#region ArtikelFacade AttachedProperty
public static ArtikelFacade GetFacadeRootParent(DependencyObject obj)
{
return (ArtikelFacade)obj.GetValue(FacadeRootParentProperty);
}
public static void SetFacadeRootParent(DependencyObject obj, ArtikelFacade value)
{
obj.SetValue(FacadeRootParentProperty, value);
}
/// <summary>
/// Die Attached Property gibt die aktuelle Instanz von ArtikelFacade zurück. Damit können wir im XAML von jeder bleliebigen Stelle aus, auf die Facade zugreifen.
/// </summary>
public static readonly DependencyProperty FacadeRootParentProperty =
DependencyProperty.RegisterAttached("FacadeRootParent", typeof(ArtikelFacade), typeof(ArtikelFacade), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
#endregion
Im Window legen wir den DataContext auf unser ModelView bzw. Facade.
Das Window beinhaltet außerdem eine Funktion bzw. Event mit der man weitere Artikel hinzufügen kann.
Window
public Window1()
{
InitializeComponent();
DataContext = new ArtikelFacade();
}
private void bu_Click(object sender, RoutedEventArgs e)
{
ArtikelDetails artikeldetails = new ArtikelDetails();
Random random = new Random();
int num = random.Next(9000);
artikeldetails.ArtikelNr = num;
artikeldetails.Artikelname = "artikelname nr " + (DataContext as ArtikelFacade).ArtikelList.Count;
(DataContext as ArtikelFacade).ArtikelList.Add(new Artikel() { Foto = "Defaul.jpg", Artikeldetails=artikeldetails });
}
{
InitializeComponent();
DataContext = new ArtikelFacade();
}
private void bu_Click(object sender, RoutedEventArgs e)
{
ArtikelDetails artikeldetails = new ArtikelDetails();
Random random = new Random();
int num = random.Next(9000);
artikeldetails.ArtikelNr = num;
artikeldetails.Artikelname = "artikelname nr " + (DataContext as ArtikelFacade).ArtikelList.Count;
(DataContext as ArtikelFacade).ArtikelList.Add(new Artikel() { Foto = "Defaul.jpg", Artikeldetails=artikeldetails });
}
Im XAML sind jeweils die DataTemplates definiert. Die Daten werden in einer ListView angezeigt. Die Funktion zum Artikel hinzufügen wird beim betätigen des Buttons ausgelöst.
Im Window Tag weisen wir unserer Attached Property den DataContext zu.
local:ArtikelFacade.FacadeRootParent="{Binding}"
Nach dem die Attached Property den DataContext zugewissen bekommen hat, können wir diese verwenden.
Im DataTemplate zeigen wir zusätzlich zu den Artikel Informationen die Gesamtanzahl der Artikel an. Da diese Information in der Facade gespeichert ist, und der aktuelle DataContext des DataTemplates der Artikel ist, benötigen wir jetzt unsere AttachedPropertie.
Mit der AttachedPropertie können wir auf das ViewModel bzw. auf die Facade zugreifen und von dort aus auf die Propertie die die gesamt anzahl der gespeicherten Artikel zurückgibt.
<Window
x:Class="ViewAttachedProperty.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ViewAttachedProperty"
local:ArtikelFacade.FacadeRootParent="{Binding}"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ArtikelDetails}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Artikelname}" Background="Aqua"></TextBlock>
<TextBlock Text="Artikel gesamt" Margin="5,0,0,0"></TextBlock>
<TextBlock Background="AliceBlue" Margin="5,0,0,0" Text="{Binding RelativeSource={RelativeSource Self}, Path=(local:ArtikelFacade.FacadeRootParent).AnzahlArtikel}"></TextBlock>
</StackPanel>
<TextBlock Text="{Binding ArtikelNr}"></TextBlock>
<TextBlock Text="{Binding Vorrat}"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Artikel}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Foto}"></TextBlock>
<ContentPresenter Content="{Binding Path=Artikeldetails}"></ContentPresenter>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListView x:Name="_ArtikelListBox" ItemsSource="{Binding ArtikelList}"></ListView>
<Button x:Name="bu" Click="bu_Click">Artikel hinzufügen</Button>
</StackPanel>
</Window>
So können wir von überall auf die View zugreifen.x:Class="ViewAttachedProperty.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ViewAttachedProperty"
local:ArtikelFacade.FacadeRootParent="{Binding}"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ArtikelDetails}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Artikelname}" Background="Aqua"></TextBlock>
<TextBlock Text="Artikel gesamt" Margin="5,0,0,0"></TextBlock>
<TextBlock Background="AliceBlue" Margin="5,0,0,0" Text="{Binding RelativeSource={RelativeSource Self}, Path=(local:ArtikelFacade.FacadeRootParent).AnzahlArtikel}"></TextBlock>
</StackPanel>
<TextBlock Text="{Binding ArtikelNr}"></TextBlock>
<TextBlock Text="{Binding Vorrat}"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Artikel}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Foto}"></TextBlock>
<ContentPresenter Content="{Binding Path=Artikeldetails}"></ContentPresenter>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListView x:Name="_ArtikelListBox" ItemsSource="{Binding ArtikelList}"></ListView>
<Button x:Name="bu" Click="bu_Click">Artikel hinzufügen</Button>
</StackPanel>
</Window>
Schlüsselwörter: WPF, C#, XAML
zuletzt geändert: 24. August 2009 14:44
Link zu diesem Artikel: (in die Zwischenablage)
(c) 2011 | Impressum |
| Empfehlenswerte Blog Einträgebaska.jpg)