how can I get access to a parent's DataContext?
I've got an UserControl containing 3 buttons, which I want to use for several different UserControls - so the user has always the same actions available.
When clicked on button 'Add' I need to do something inside the current DataContext, which isn't much of a hassle since I can just do the following:
public void CtrlClicked(object sender, RoutedEventArgs e){
Button btn = sender as Button;
MyClass2 c2 = btn.DataContext as MyClass2;
c2.CallCustomMethod();
}
When the button 'Del' is clicked I want to delete the object MyClass2 out of a List<MyClass2> which is held in MyClass1.
In order to do that I need to have access to MyClass1.
My UI (pseudo code):
Window (DataContext = base)
Grid
UserControl uc1 (DataContext = base.MyClass1)
Grid
ListView
ListView.DataTemplate
UserControl uc2 (DataContext = base.MyClass1.MyClass2)
Grid
UserControl ucButtons
Grid
UserControl uc2
ListView.DataTempate
ListView.PanelTemplate
UniformGrid
ListView.PanelTemplate
ListView
Grid
UserControl uc1
Grid
Window
So how can I get access to the MyClass1-objext?
I found out that I can walk the tree using .Parent, but can only do that to a certain point:
Grid gScheduleControlBar = btn.Parent as Grid;
UserControl ucScheduleControlBar = gScheduleControlBar.Parent as UserControl;
Grid gDay = ucScheduleControlBar.Parent as Grid;
UserControl ucDay = gDay.Parent as UserControl;
//ucDay.Name confirms it's the userControl defined
Grid grid = ucDay.Parent as Grid;
// grid.Name="" and grid.Parent = null
so from here there is no further way upwards, which means I can't pass the UserControl 'border'.
Any ideas?
As fallback-option there is of course the way of storing a reference of MyClass1 in MyClass2.
EDIT => Final Result:
<Button x:Name="Del" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor AncestorType=UserControl AncestorLevel=3}}"
If you want to do this via Bindings, you can use RelativeSource={RelativeSource Mode=FindAncestor AncestorType=yourNamespace:YourType}, from code you can use the VisualTreeHelper to get the visual parent of any control.
If there are multiple parents of that type in your hierarchy can can additionally specify an AncestorLevel. In the example you included, it looks like AncestorType=UserControl and AncestorLevel=2 should work.
Related
I have several TabControls defined in my XAML. I would like my ViewModel to be aware of the TabItem name or the index of the TabItem that is selected.
I also have a ScrollViewer that i would like to always scroll to the bottom when ever a button is pressed.
I should be able to solve both of the above issues if i could somehow get access to the elements in my code.
How can i acheve something like this:
var tabIndex = this.GetElement<TabControl>("NameOfSomeTabControl").SelectedIndex;
var scrollViewer = this.GetElement<ScrollViewer>("NameOfSomeScrollViewer");
scrollViewer.VerticalScrollBarValue = scrollViewer.VerticalScrollBarMaximum;
Edit: code for xaml, viewModel code
Edit 2:
Looks like i am able to get the instance of the element from the window class, however i'm still not sure how to pass the reference to the ViewModel.
Edit 3: I can achieve the scroll viewer going to the bottom automatically using the code below. however, once that method is invoked it seems like the scrolling gets disabled.
var tbRaw = this.Get<TextBlock>("tbRawOutput");
tbRaw.PropertyChanged += (s,e) => {
var svRaw = this.Get<ScrollViewer>("svRawOutput");
svRaw.Offset = new Vector(svRaw.Offset.X, svRaw.Extent.Height -svRaw.Viewport.Height);};
An easier way to do this might be to use the DataContextChanged event handler in your main Window class:
public MainWindow()
{
InitializeComponent();
DataContextChanged += (object sender, EventArgs wat) =>
{
// here, this.DataContext will be your MainWindowViewModel
};
}
Then you can attach more event handlers/use getters and setters on the view model from the Window
i already searched few stackoverflow threads related thisbut i have problem in code plz review my code and tell me where is the problem i am stuck and do not where is the problem i am new here please help me out
public partial class MainWindow : Window
{
Grid MainGrid = new Grid();
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
TextBox dynamicTextBox = new TextBox();
dynamicTextBox.Text = "Type Partnumber";
Grid.SetRow(dynamicTextBox, 1);
Grid.SetColumn(dynamicTextBox, 0);
this.MainGrid.Children.Add(dynamicTextBox);
}
}
By using the button_Click event you are adding the dynamic TextBox to the MainGrid and that code looks fine. But the problem is that this.MainGrid is not in the Present UI, its similar to the dynamically created TextBox, since you ware defined it in the code-behind(see the definition above the constructor Grid MainGrid = new Grid();).
Consider that canContainer is a canvas defined in the xaml as like the following,
<Canvas Height="319" Margin="0"
Width="517"
Name="canContainer"/>
To overcome this you can choose any of the following method.
1.Add The MainGrid to the UI. it will add the grid to the canvas, keep in mind the dynamic textBox already added to the Grid.
which means the code should be like this -->
this.canContainer.Children.Add(MainGrid);
2.Add dynamicTextBox to any other parent which is present in the UI.
this.canContainer.Children.Add(dynamicTextBox);
You you are using this method then need not to define and adding MainGrid
I'm new at WPF/C# and I have a question about binding Collections using Entity Framework.
I have a base Entity"Order" (Commande in french) and I used Inheritance per type to regroup them in fonctionnal topics : by instance : Order_Doc, Order_Check , ...
I put controls on tabitem with each item containing a panel of controls I'd like to bind with child entity fields.
So I'm searching an elegant solution to fill these tab items with the associated Child Entity.
As yet, I've managed to bind controls with data belonging to the base Class Order and to walk through the collection.
I've declared a CollectionViewSource based on Orders.
<Window.Resources>
<CollectionViewSource x:Key="ViewSourceCommandes" d:DesignSource="{d:DesignInstance src:Commande, CreateList=True}"/>
</Window.Resources>
When the window is loaded I Initialise the collection of Orders (commandes)
CommandesViewSource = ((CollectionViewSource)(this.FindResource("ViewSourceCommandes")));
ObjectQuery<Commande> CommandesQuery = this.GetCommandesQuery(contexte);
CommandesViewSource.Source = CommandesQuery.Execute(MergeOption.AppendOnly);
The function GetCommandesQuery is simple :
private ObjectQuery<Commande> GetCommandesQuery(ModelContainer CommandeEntities)
{
ObjectQuery<Commande> CommandesQuery = CommandeEntities.Commandes;
return CommandesQuery;
}
I can walk through the collection by this function :
private void buttonFind_Click(object sender, RoutedEventArgs e)
{
Int32 NumCommande = Convert.ToInt32(textBoxNumCommande.Text);
Commande commandeItem = contexte.Commandes.Where("it.NumCommande = #NumCommande", new ObjectParameter("NumCommande", NumCommande)).FirstOrDefault();
CommandesViewSource.View.MoveCurrentTo(commandeItem);
}
Some controls on my main Window are binded on "Commande" fields : I can get the right content for them.
But there's also in this main Window a TabControl in which I'd like to feed TabItems with
inherited classes as Commande_Doc, Commande_Check, ... and to bind controls on their fields.
What is the best way to implement this elegantly ? I'm a bit lost ...
Thanks by advance.
Is there any way to access xaml class reference/name through its child ui control ?
the code is--->
private void AvailableItemListScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
_listScrollViewer = sender as ScrollViewer;
Binding binding = new Binding();
binding.Source = _listScrollViewer;
binding.Path = new PropertyPath("VerticalOffset");
binding.Mode = BindingMode.OneWay;
this.SetBinding(ListVerticalOffsetProperty, binding); <---//(this)
}
Where this (shown above) referring the parent class which contains _listScrollViewer control, but I want to write AvailableItemListScrollViewer_Loaded method for all the pages of my app, so I am putting it into a separate class, but here this listener only receiving that ui element reference, so how can i get the parent class ? So I can write the last line of code above something like this--->
(class reference).SetBinding(ListVerticalOffsetProperty, binding)
Why not to use XAML binding here ? Smth like
<Page ListVerticalOffset="{Binding Path="VerticalOffset"
ElementName="ScrollViewerName"}/>
Simply copy this binding to every page you need with according ElementName
I have a UserControl say Stock and it has a Button called Display
<Button Command="{Binding DisplayCommand}" CommandParameter="StockGroups">Display</Button>
Now when i Click this button it should add an another UserControl named Display to the Canvas which is in HomeWindow and should pass the CommandParameter to the Display userControl.
private DelegateCommand<string> _displayCommand;
public virtual void DisplayExecuted(string param){}
public ICommand DisplayCommand
{
get
{
if (_displayCommand == null)
_displayCommand = new DelegateCommand<string>(new Action<string>(DisplayExecuted));
return _displayCommand;
}
}
An alternative method which is more MVVM-ish would be to have a boolean property named ShouldDisplayControl, which is then bound to the control's Visibility property (using the [BooleanToVisibilityConverter]) 1), while passing the CommandParameter as a second property, maybe ControlParameter, which the control is also bound to.
This is not an operation that should involve the ViewModel, since it does not manipulate any model data.
Instead of a ViewModel command, consider merely handling the button's OnClick in the code-behind of the xaml.
In your HomeWindow.xaml.cs file:
protected override void Display_OnClick(object sender, EventArgs e) {
var buttonName = ((Button)sender).Name; // gets the name of the button that triggered this event
var displayControl = new DisplayControl(); // your user control
displayControl.param = buttonName; // set the desired property on your display control to the name of the button that was clicked
((Canvas)Content).Children.Add(displayControl); // 'Content' is your Canvas element
}
And in your HomeWindow.xaml file:
<Button x:Name="StockGroups" Click="Display_OnClick" Text="Display" />
That should get you what you want, without needing to create and invoke a command in the viewmodel. The name of the clicked button will be set to the specified property in your userControl, and an instance of the control will be created inside the Canvas.