Hi,
ich möchte von einer .xaml.cs auf die Daten der zugehörigen .cs Datei zugreifen von welcher sich die .xaml Datei die Daten holt. Ich verstehe die Reihenfolge der Zugriffe grade nicht. In der .cs Datei wird eine Liste erstellt. Diese Liste wird von der .xaml Datei in den Content eines HeaderedContentControl geladen dies funktioniert mit folgendem Code:
<HeaderedContentControl Grid.Column="2"
Header="Rechte Seite"
DataContext="{Binding}"
Content="{Binding Path=RightDimensionExplorerList, ValidatesOnDataErrors=True}"
ContentTemplate="{StaticResource DimExList}"
Foreground="#FF000000"
Margin="0,0,5,0"
x:Name="RightSide" />
Nun möchte ich auf eben diese Liste in der Code-Behind Datei, der .xaml.cs Datei zugreifen. Wenn ich aber nun mit folgendem Code:
List<DimensionExplorerVM> explorerList = (List<DimensionExplorerVM>)RightSide.Content;
versuche auf den Content zuzugreifen ist dieser immer null. Was mache ich falsch? Müsste nach dem
InitializeComponent();
nicht eigentlich die entsprechende Liste (die auch beim Debuggen in der .cs Datei gefüllt wird) in dem Content zu finden sein?
Hallo N da G,
beachte, dass es partielle Klassen sind!
zero_x
zero_x | <span style="font-size: 10;">my</span><span style="font-size: 10;">CSharp</span><span style="font-size: 10;">.de</span> - gemeinsam mehr erreichen
Für längere Zeit inaktiv.
Genau das ist mein Problem ich verstehe den Zusammenhang zwischen den partiellen Klassen und der eigentlichen Hauptklasse nicht so ganz. Gibt es denn eine Möglichkeit zwischen den Klassen die Daten auszutauschen?
ich möchte von einer .xaml.cs auf die Daten der zugehörigen .cs Datei zugreifen von welcher sich die .xaml Datei die Daten holt.
.
.
.DataContext="{Binding}"
Damit hast du doch schon den Kontext deiner .cs Datei. Das was du in den DataContext setzt kannst du in der .xaml verwenden.
Genau das ist mein Problem ich verstehe den Zusammenhang zwischen den partiellen Klassen und der eigentlichen Hauptklasse nicht so ganz.
Partielle Klassen = die Konstruktion einer Klasse kann auf mehrere Dateien zerlegt werden. Das geschieht standardmäßig wenn du eine Seite in dein Projekt hinzufügst. Bsp: Window.xaml und Window.xaml.cs
Grüßle
Jéré
Erstell die Liste im Code behind (.xaml.cs) und setze dann diese auf den DataContext
var myList = new List<string>();
public XY() {
Initialize();
this.DataContext = myList;
}
=> xaml:
<... DataContext="{Binding}"... />
Ansonsten schau dir mal Tutorials zu Xaml, Code behind und DataContext an.. (wahrscheinlich wirst du sehr schnell auf das MVVM Stichwort stossen, aber dazu gibt es auch viele Infos im Netz...)
Gruss
Danke erstmal für die vielen tipps. Aber mein Problem ist das ich die Liste nicht im Code-Behind erstellen kann. Sie wird in der .cs Datei erstellt und sie wird auch in der .xaml Datei eingebunden und verwendet. Ich möchte aber folgendes machen:
Ich möchte die Liste die im DataKontext gepseichert wird im Code-Behind abrufen um dynamisch die .xaml zu verändern. Generell kann man das so beschreiben, dass die Liste zur Zeit in nur einer Column angeordnet wird. Die Liste kann jedoch beliebig viele Expander enthalten und diese Expander werden dann immer zentriert angeordnet wenn man sie aufklappt. Daher möchte ich die Expander auf mehrere Columns aufteilen und das geht doch nur im Code-Behind oder? Daher brauche ich aber die Daten die in der Liste drinne sind und auf die versuche ich zuzugreifen. Aber wie oben erwähnt ist der Content nach dem Aufruf von
initializeComponent()
immer null.
Ich habe mich auch schon durch einige WPF-Tutorials durchgearbeitet und bin immer wieder darauf gestoßen, dass man nur die Oberfläche dynamisch verändern kann wenn man auf xaml verzichtet. Stimmt das oder gibt es da doch eine Möglichkeit?
Poste mal dein Code, vielleicht versteht man dann dein Problem... (so wie ich das verstehe ist deine cs Klasse eine typische ViewModel Klasse, die du auch direkt an den DataContext hängen könntest...)
Und das die oberfläche nicht durch xaml dynamisch verändert werden kann, hängt wohl stark von den gewünschten änderungen ab...
wie gesagt, dein code würde wohl helfen...
Ich zeige einfach mal ein paar Snippets die benutzt werden.
Also folgendes wird in der Klasse RulePageVM aufgerufen:
public RuleVM TheRule { get; set; }
foreach (DimensionVM dim in TheRule.RightSide)
{
DimensionExplorerVM dimEx = new DimensionExplorerVM(dim);
RightDimensionExplorerList.Add(dimEx);
dimEx.PropertyChanged += OnSelectionChangedHandler;
dim.PropertyChanged += OnSelectionChangedHandler;
}
RightSide wird dabei in der Klasse RuleVM gefüllt.L:
public List<DimensionVM> RightSide { get; set; }
foreach (Dimension dim in rule.RightSide)
RightSide.Add(new DimensionVM(dim));
Mit jeder Dimension wird ein DimensionExplorer initialisiert und dieser hat dann folgenden XAML-Code:
<UserControl x:Class="RuleEditor.View.DimensionExplorer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:RuleEditor.View"
xmlns:vm="clr-namespace:RuleEditor.ViewModel"
xmlns:attached="clr-namespace:RuleEditor"
xmlns:Models="clr-namespace:RuleEditor.ViewModel.Models"
VerticalAlignment="Top">
<UserControl.Resources>
<!--NodeList-->
<!-- Hier werden die Knoten für die neue(n) RegelEntität(en) eingetragen-->
<DataTemplate x:Key="NodeList">
<ListView ItemsSource="{Binding ., ValidatesOnDataErrors=True}"
MaxHeight="450"
Background="AliceBlue"
Margin="0,4,0,0"
SelectionMode="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type HeaderedContentControl}, AncestorLevel=3},Path=DataContext.SelectionMode}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected"
Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<!-- CommandBind auf den DoubleClick Command im DimExplVM
attached:ClickCommands.MouseDoubleClickCommand="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}, AncestorLevel=2},Path=DataContext.DoubleClick}"
attached:ClickCommands.MouseEventParameter="{Binding RelativeSource={RelativeSource Self}}"-->
<!-- ParameterBind auf dieses Element-->
<TextBlock Text="{Binding Name}"
Margin="2.5,0,10,0">
<TextBlock.ToolTip>
<TextBlock Text="{Binding Description}" />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
<LinearGradientBrush x:Key="LoadInProgressBrush"
StartPoint="0,0.5"
EndPoint="1,0.5">
<GradientStop Color="#40757F"
Offset="0" />
<GradientStop Color="#00D7FF"
Offset="0.97" />
<GradientStop Color="#80EBFF"
Offset="0.999" />
<GradientStop Color="#E6FBFF"
Offset="1" />
</LinearGradientBrush>
<Style x:Key="progressBarBorderStyle"
TargetType="Border">
<Style.Resources>
<LinearGradientBrush x:Key="progressBarBackgroundBrush"
StartPoint="0.5,0"
EndPoint="0.5,1">
<GradientStop Color="#88000000"
Offset="0.1" />
<GradientStop Color="#CC000000"
Offset="0.9" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="progressBarBorderBrush"
StartPoint="0.5,0"
EndPoint="0.5,1">
<GradientStop Color="#18000000"
Offset="0.1" />
<GradientStop Color="#08000000"
Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background"
Value="{StaticResource progressBarBackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{StaticResource progressBarBorderBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="CornerRadius"
Value="8" />
<Setter Property="Margin"
Value="2,4" />
</Style>
<attached:WidthConverter x:Key="WidthConv" />
<!-- der Ladebalken-->
<DataTemplate x:Key="progBar">
<Border x:Name="progressBar"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext}"
Style="{StaticResource progressBarBorderStyle}">
<Grid Width="{Binding ElementName=containerName, Path=ActualWidth}">
<StackPanel Orientation="Horizontal">
<Rectangle x:Name="progressIndicator"
Fill="{StaticResource LoadInProgressBrush}"
HorizontalAlignment="Left"
Margin="1"
RadiusX="8"
RadiusY="8">
<!-- die Breite des Ladebalkens mittels Converter berechnen -->
<Rectangle.Width>
<MultiBinding Converter="{StaticResource WidthConv}">
<Binding Path="PercentComplete" />
<Binding ElementName="progressBar"
Path="ActualWidth" />
</MultiBinding>
</Rectangle.Width>
</Rectangle>
</StackPanel>
<TextBlock x:Name="containerName"
Text="{Binding Name}"
VerticalAlignment="Center"
Foreground="White" />
</Grid>
<!-- prozentualer Ladeanteil als Tooltip anzeigen -->
<Border.ToolTip>
<ToolTip>
<Grid>
<TextBlock>
<TextBlock Text="{Binding PercentComplete}"
Margin="0,0,-2,0" />
<Run>% finished</Run>
</TextBlock>
</Grid>
</ToolTip>
</Border.ToolTip>
</Border>
</DataTemplate>
<!-- Dimension Tree -->
<DataTemplate x:Key="DimensionTree">
<!-- gleiches Binding wie Elternelement -->
<!-- x:Type beschreibt je nach Type des Elementes die Darstellung,
Knoten besitzen z.B. keine weiteren Kindelemente,
daher fehlt dort der Itemsource-->
<v:TreeViewEx ItemsSource="{Binding}"
Background="AliceBlue"
Name="TheTreeView"
MaxHeight="443">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<StackPanel Margin="10,2,0,0">
<WrapPanel Name="TVItem">
<ToggleButton Name="TVexpander"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
Content="+"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}, AncestorLevel=2}, Path=DataContext.CheckExpanderCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}" />
<ContentPresenter x:Name="PART_Header"
ContentSource="Header" />
</WrapPanel>
<Border>
<ItemsPresenter Name="TVchildren"
Visibility="Collapsed" />
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded}"
Value="True">
<Setter TargetName="TVchildren"
Property="Visibility"
Value="Visible" />
</DataTrigger>
<!-- Den Expander nur aktivieren, wenn das Element Ebenen als Kinder hat-->
<DataTrigger Binding="{Binding IsNode}"
Value="True">
<Setter TargetName="TVexpander"
Property="Visibility"
Value="Collapsed" />
</DataTrigger>
<Trigger Property="Visibility"
Value="Collapsed">
<Setter TargetName="TVexpander"
Property="IsEnabled"
Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight"
Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="FontWeight"
Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<!-- Binding LayerVM-->
<HierarchicalDataTemplate DataType="{x:Type Models:LayerVM}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<ContentControl DataContext="{Binding}"
ContentTemplate="{StaticResource progBar}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type Models:NodeVM}" />
</TreeView.Resources>
</v:TreeViewEx>
</DataTemplate>
</UserControl.Resources>
<!-- Die einzelne Dimension. Jede Dimension ist komplett ein/ausklappbar.-->
<Expander x:Name="DimExpander">
<Expander.Header>
<ContentControl DataContext="{Binding}"
ContentTemplate="{StaticResource progBar}" />
</Expander.Header>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<StackPanel Margin="10,2,0,0">
<WrapPanel Name="DimItem">
<!--Den Button überschreiben, damit die Kinder (in der Ebenenauswahl oder Knotenauswahl) beim Öffnen dynamisch geladen werden.-->
<ToggleButton Name="DimExpButton"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
Content="+"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=DataContext.CheckExpanderCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
DataContext="{Binding Dimension}" />
<ContentPresenter x:Name="PART_Header"
ContentSource="Header" />
</WrapPanel>
<Border>
<ContentPresenter Name="DimChildren"
Visibility="Collapsed" />
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<!-- Inhalt des Expanders (Ebenen-, Knotenauswahl,...) sichtbar machen, wenn die Dimension geöffnet wurde. -->
<DataTrigger Binding="{Binding Dimension.IsExpanded}"
Value="True">
<Setter TargetName="DimChildren"
Property="Visibility"
Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Style>
<Border BorderBrush="Black"
BorderThickness="1"
Margin="0,0,5,5"
VerticalAlignment="Top">
<Grid Background="#D6ECFF" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<!-- Abschnitt Ebenenauswahl-->
<Expander Header="Layers"
Name="DimTree"
Grid.Column="0"
Content="{Binding Dimension.Children}"
ContentTemplate="{StaticResource DimensionTree}">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<StackPanel Margin="10,2,0,0">
<WrapPanel Name="LayerItem">
<ToggleButton Name="LayerExpander"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
Content="+"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}, AncestorLevel=2}, Path=DataContext.CheckExpanderCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}" />
<ContentPresenter x:Name="PART_Header"
ContentSource="Header" />
</WrapPanel>
<Border>
<!-- Ebenen sollen von Anfang an angezeigt werden.-->
<ContentPresenter Name="LayerChildren"
Visibility="Visible" />
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Dimension.ChildIsNode}"
Value="True">
<Setter TargetName="LayerItem"
Property="Visibility"
Value="Collapsed" />
<Setter TargetName="LayerChildren"
Property="Visibility"
Value="Collapsed" />
</DataTrigger>
<!-- Normales Ein- und Ausklappen implementieren. -->
<Trigger Property="IsExpanded"
Value="True">
<Setter TargetName="LayerChildren"
Property="Visibility"
Value="Visible" />
</Trigger>
<Trigger Property="IsExpanded"
Value="False">
<Setter TargetName="LayerChildren"
Property="Visibility"
Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Style>
</Expander>
<!-- Abschnitt Knotenauswahl-->
<Expander Header="Nodes"
Name="NodeSelection"
Grid.Column="1"
DataContext="{Binding}"
IsExpanded="True"
IsEnabled="{Binding IsEnabled}"
Content="{Binding CurrentSelectedItemsChildren}"
ContentTemplate="{StaticResource NodeList}"
Margin="0,0,2.5,2.5"
VerticalAlignment="Top" />
<!--<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="50*" />
<RowDefinition Height="50*" />
</Grid.RowDefinitions>
<Button Name="AddButton"
Grid.Row="0"
Content=">>"
Command="{Binding AddCommand}" />
<Button Grid.Row="1"
Content="&lt;&lt;"
Command="{Binding RemoveCommand}" />
</Grid>-->
<!-- Abschnitt Hinzuzufügende Knoten-->
<!--<Expander Grid.Column="3"
MinHeight="275"
Header="Hinzuzufügende Knoten"
Name="NodesToBeAdded"
DataContext="{Binding}"
Content="{Binding NodesToBeAdded}"
ContentTemplate="{StaticResource NodeList}"
IsExpanded="True" />-->
</Grid>
</Border>
</Expander>
</UserControl>
Diese Dimensions werden dann in der Liste (RightDimensionExplorerList siehe Anfang) gespeichert. Diese Liste wird dann in der RulePage.xaml gebunden.
<HeaderedContentControl Grid.Column="2"
Header="Rechte Seite"
DataContext="{Binding}"
Content="{Binding Path=RightDimensionExplorerList, ValidatesOnDataErrors=True}"
ContentTemplate="{StaticResource DimExList}"
Foreground="#FF000000"
Margin="0,0,5,0"
x:Name="RightSide" />
Soviel zum Code aber wenn ich nun diese Liste im Code-Behind nach dem
initializeComponent()
über den Content aufrufen will ist dieser immer null.
Ist immer noch nicht klar was genau ich machen möchte oder weiß hier niemand wie ich zu einer Lösung kommen kann?
Nach dem InitializeComponent kann es durchaus sein, dass die Liste noch null ist, schau dir das Ganze mal nach dem Loaded-Event an, da müsste sie dann gesetzt sein.
Alternativ kannst du dich auch an das Content-DependencyProperty "ranhängen" und auf Änderungen an diesem reagieren.
Edit: Warum greifst du nicht direkt auf den DataContext zu (der ja deine Liste enthält), sobald dieser gesetzt ist (hier kannst du einfach auf DataContextChanged reagieren), sondern gehst den Umweg über das HeaderedContentControl?
There are 10 kind of people, those who understand binary and those who don't.
Hallo N da G,
Ist immer noch nicht klar was genau ich machen möchte oder weiß hier niemand wie ich zu einer Lösung kommen kann?
ehrlich gesagt halte ich es für eine Zumutung, sich den gesamten XAML-Code, den du gepostet hast, durchzulesen. Abgesehen davon wäre es hilfreich gewesen, ihn in XML-Tags zu packen und entsprechend einzurücken.
m0rius
666 Beiträge, Achtung, diabolisch 😛
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Vielen Dank für die schnelle Antwort. Leider kann ich damit zur Zeit noch nicht ganz so viel anfangen. Wäre es möglich das du ein Beispiel bereit stellen könntest habe noch nicht ganz so viel mit xaml und code behind gemacht und versuche ein schon fertiges Programm zu bearbeiten was es nicht unbedingt leichter macht.
@m0rius: Tut mir leid ich wusste nicht wie ich die einrückung machen kann. Ich habe den Code lediglich mit eingestellt damit jemand der sich eventuell sehr gut damit auskennt die relevanten Stellen finden kann. Wie eben erwähnt kenne ich mich mit xaml noch nicht sehr gut aus.
Wie funktioniert das denn genau mit dem DataContextChanged? (Falls es zu viele Umstände macht kann ich es jedoch selber googlen). Aber schonmal Danke für den Tipp.
Naja, im Konstruktor schreibst du sowas wie
this.DataContextChanged += OnDataContextChanged;
Dazu noch eine entsprechende Methode
private void OnDataContextChanged(object sender, EventArgs e)
{
var list = (this.DataContext as >>HierDenTypDerDieListeEnthältEinfügen<<).RightDimensionExplorerList;
// Da hast du die Liste.
}
Ungetestet hier im Forum geschrieben, evtl passen die EventArgs nicht.
There are 10 kind of people, those who understand binary and those who don't.
Ok super vielen Dank werde ich mal ausprobieren und nach den genauen Eventargs kann ich ja googlen.
Klingt auf jeden Fall schonmal so als wenn es damit funktionieren könnte.
Gruß N da G
Hallo N da G,
deinen C#-Code packst du in CSHARP-Tags, der XAML-Code gehört in XML-Tags. Deutlich besser zu lesen wird dieser natürlich, wenn er eingerückt eingefügt wird.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg