Access the child control of a user control - wpf

I have created a user control that consists of a expander, listbox and checkboxes. I am not able to access the checkboxes (child control) and I want to generate the number of expanders based on the number of rows in a table dynamically. Can anyone suggest the possible solutions to

This is extremely vague. In most cases you would just expose some of the internal control's properties, e.g. if you want to create dynamic content you would expose the ItemsSource and ItemTemplate of an internal ListBox of whatever you use so it can be set from outside, e.g.
<UserControl x:Class="Test.UserControls.Bogus" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" Name="control">
<StackPanel>
<TextBlock Text="Lorem Ipsum:" />
<ItemsControl ItemsSource="{Binding ElementName=control, Path=ItemsSource}"
ItemTemplate="{Binding ElementName=control, Path=ItemTemplate}" />
</StackPanel>
</UserControl>
public partial class Bogus : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = ItemsControl.ItemsSourceProperty.AddOwner(typeof(Bogus));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty = ItemsControl.ItemTemplateProperty.AddOwner(typeof(Bogus));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public Bogus()
{
InitializeComponent();
}
}
Usage:
<uc:Bogus ItemsSource="{Binding Data}">
<uc:Bogus.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Foreground="Red" />
</DataTemplate>
</uc:Bogus.ItemTemplate>
</uc:Bogus>
You can of course also encapsulate a lot of logic which you do not need exposed.
Since you want a varying amount of expanders you might have an ItemsControl (unlike a ListBox it has no selection) which already defines an ItemTemplate which contains an expander. You probably can also create a partial template as shown in this answer of mine.

Sounds like you need to navigate the visual tree. The simplest way of doing this is via Linq-to-VisualTree. To find all the CheckBoxes that are a child of 'this', use the following query:
IEnumerable<CheckBox> checks = this.Descendants<CheckBox>().Cast<CheckBox>();

Your application is running in an Application instance. Access the usercontrol components with Application.usercontrol.ComponentName if it is not a UI update. If you make UI updates, you have to run the access in a separate dispatcher thread. In that case, use BackgroundWorker.
For example, I am running my main application class MainWindow and accessing it as,
MainWindow rootWindow = Application.Current.MainWindow as MainWindow;
Now access the usercontrol and properties of components as:
rootWindow.usercontrolX.ComponentY.PropertyZ

Define properties in the child's class for each of those controls. You will be able to access them from the Parent User Control, assuming you have added the Child User Control within the Parent User Control.
Parent User Control.. SingalData is the child User Contol
<my:C1TabItem Header="Signal">
<local:SignalData Width="1036" OnSignalNameChange="SignalInputTab_OnSignalNameChange" Loaded="SignalInputTab_Loaded" Height="353" VerticalAlignment="Top" MinHeight="353" HorizontalAlignment="Left"></local:SignalData>
In the Child User Contorl class, if you have a component named tabProductList you add a property -
public C1.WPF.C1TabControl TabProductList
{
get { return this.tabProductList; }
}
And finally, from your parent class you can reference it as -
C1TabItem tbItem = (C1TabItem)c1TabControl1.SelectedItem;
SignalData sigInp = (SignalData)tbItem.Content;
if (sigInp.TabProductList.SelectedIndex == 0)
{
....

Related

Setting separate data binding for controls in User Control

I am relatively new to WPF and I am attempting to design my first project using MVVM pattern.
Now I have the main window as my view.I have an user control on this.Now the user control has 2 expanders and each need to pull it's own data.
<Expander Header="Electrical Components" Name="EC"
IsExpanded="True">
<ItemsControl ItemsSource="{Binding ElecViewModel.ToolBoxItems }">
............
<Expander Header="Structural Components" Name="SC"
IsExpanded="True">
<ItemsControl ItemsSource="{Binding StructViewModel.ToolBoxItemsS}">
.............
So I created 2 view models with the idea that I can do a data bind for each of the expanders. The idea is to pull some images under each expander.
First expander I attach to this view model
public class ElecViewModel
{
private List<ToolBoxData> toolBoxItems = new List<ToolBoxData>();
public ElecViewModel()
{
toolBoxItems.Add(new ToolBoxData("../Images/Inverter.jpg", typeof(InverterDesignerItemViewModel)));
toolBoxItems.Add(new ToolBoxData("../Images/Recombiner.jpg", typeof(RecombinerDesignerItemViewModel)));
}
public List<ToolBoxData> ToolBoxItems
{
get { return toolBoxItems; }
}
}
..And second one to this view model
public class StructViewModel
{
private List<ToolBoxData> toolBoxItemsS = new List<ToolBoxData>();
public StructViewModel()
{
toolBoxItemsS.Add(new ToolBoxData("../Images/SafetySwitch.jpg", typeof(SafetySwitchDesignerItemViewModel)));
toolBoxItemsS.Add(new ToolBoxData("../Images/ScadaPanel.jpg", typeof(ScadaDesignerItemViewModel)));
}
public List<ToolBoxData> ToolBoxItemsS
{
get { return toolBoxItemsS; }
}
}
Now my first expander is getting loaded with the correct images.whereas the second one doesn't. The public list of the second view model does not get hit even though I have binded to it like the first list ? Is there an obvious reason for that ?I tried setting the binding source of second expander to first list and it gets populated suggesting the list I am creating for second one has some issue in getting binded to a control. Please suggest as i am not able to identify any obvious issue.
In your condition don't set the DataContext of your usercontrol explicitly. Assuming your MainViewModel has two properties for ViewModel instances ElecViewModel and StructViewModel. And MainViewModel is DataContext of your Window, you can bind these expanders like
<Expander Header="Electrical Components" Name="EC"
IsExpanded="True">
<ItemsControl ItemsSource="{Binding ElecViewModel.ECData}">
............
<Expander Header="Structural Components" Name="SC"
IsExpanded="True">
<ItemsControl ItemsSource="{Binding StructViewModel.SCData}">
.............

Bind user control dependency properties in MVVM style Windows Phone app

I'm having some issues with binding some custom controls in a Windows Phone app right now. Usually this is never an issue but apparently my mind can't comprehend this today.
So I'm doing an MVVM style setup which is good. I have my page with a view and also a viewmodel. Now on a WebClient callback I assign the dataContext of my view to the list of models in my ViewModel, nice and simple thus far...now in my view I created a ListBox with a custom control in the datatemplate which is basically a cell in the list. I once again set my user controls dataContext to binding, and binding all the models values to the regular UI elements works no problem.
Here's a sample:
<Grid Grid.Column="0">
<Image Source="{Binding SmallPath}" VerticalAlignment="Top"/>
</Grid>
<Grid Grid.Column="1">
<StackPanel Margin="12,0,0,0">
<TextBlock x:Name="MemberId_TextBlock" Text="{Binding MemberId}" FontSize="28"
Margin="0,-8,0,0"
Foreground="{StaticResource PhoneForegroundBrush}"/>
<StackPanel Orientation="Horizontal" Margin="0,-11,0,0">
<TextBlock Text="{Binding DaysReported}" FontSize="42"
Margin="0,0,0,0"
Foreground="{StaticResource PhoneAccentBrush}"/>
<TextBlock Text="days" FontSize="24"
Margin="3,19,0,0"
Foreground="{StaticResource PhoneSubtleBrush}"/>
</StackPanel>
</StackPanel>
</Grid>
That's in my user control, and here's the the view where the usercontrol is housed:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ListBox Name="TopSpotter_ListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<!--<TextBlock Text="{Binding MemberId}"/>-->
<controls:TopSpotterItemControl DataContext="{Binding}"/>
<Grid Height="18"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Now this is good enough but what I want to do in my view is set data from my model like Booleans that determine whether or not I should show certain Grids etc. So if I try to set a dependency property explicitly in my control it fires and will run logic in the Getter/Setters for instance. HOWEVER if I try to set these custom objects from a binding source it won't actually set.
Here's what works:
<controls:TopSpotterItemControl ChampVisibility="True">
This way will trigger the ChampVisibility property and then in the code behind of the user control I can set visibilities.
Here's what fails but I want to work:
<controls:TopSpotterItemControl ChampVisibility="{Binding IsChamp">
In addition I can still set the DataContext to {Binding} and the result will be unchanged.
In this scenario IsChamp is part of my model that I would like to bind to this user control which I guess comes from the dataContext being set on the view from the viewModel. I'm not sure what I can do to get this so the bindings work etc. without having to set custom properties.
Finally, here's my user control:
public partial class TopSpotterItemControl : UserControl
{
public string MemberId
{
get
{
return this.MemberId_TextBlock.Text;
}
set
{
this.MemberId_TextBlock.Text = value;
}
}
public bool ChampVisibility {
set
{
if (value)
{
this.Champ_Grid.Visibility = System.Windows.Visibility.Visible;
}
}
}
public static readonly DependencyProperty MemberNameProperty =
DependencyProperty.Register("MemberId", typeof(string), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public TopSpotterItemControl()
{
InitializeComponent();
}
}
Bit long winded and I hope I made things on the issue clear. My one major hang up so far, and I'd like to abstract as much control as I can to the user control via dependency properties explicitly set in xaml, rather than setting up binding in its xaml that depend on the knowledge of a model. Thanks!
Your DependencyProperty is badly formed. (I also don't see Champ_Grid defined in your class or XAML, but I assume that is an ommission)
Setting ChampVisibility = true in code works because it is unrelated to the DependencyProperty.
You can tell easily because the default value for your DP is invalid. It will compile, but the instance constructor will through an exception if it is ever invoked.
new PropertyMetadata(null)
bool = null = exception
If you call GetValue(TopSpotterItemControl.ChampVisibilityProperty) from somewhere you can confirm all of the above.
You should make changes to instance fields in the property changed handler and declare the property like the following, it will work:
Note that the property has to change (not just be set) for the event to be raised.
public bool ChampVisibility
{
get { return (bool)GetValue(ChampVisibilityProperty); }
set { SetValue(ChampVisibilityProperty, value); }
}
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility ", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(true, (s, e) =>
{
TopSpotterItemControl instance = s as TopSpotterItemControl;
instance.Champ_Grid.Visibility = instance.ChampVisibility ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}));
Incidentally, your MemberId DependencyProperty is also completely wrong and cannot work.
Note:
The Binding on your TextBox works, because it is binding to the DataContext (your model), so it probably shows the right value.
The Dependency property in your UserControl will never be set though.
Use the propdp code-snippet in Visual Studio so you dont have to concern yourself with the complexities of Dependency Property declaration.
Also check this out for more info about Dependency Properties

Making an easy to use UserControl via Properties

in my Silverlight 4 app, I try to create a simple UserControl, which will be consumed by my Application. To keep things simple, it shall have a "header" and a placeholder, where I want to place any kind of control.
<User Control ...>
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TextBlockHeader" Text="{Binding Title}" />
<ContentPresenter x:Name="ContentPresenterObject" />
</Grid>
</UserControl>
In the code behind, I have created a property for the text of the TextBlock
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MyAccordion), null);
This way, I can set the Title property, when I use the Control in my application.
<local:MyAccordion Title="Test"/>
But it seems, that the binding at the textblock Text="{Binding Title}" doesn't make the text "Test" to be displayed as the textblocks text.
My question is: How can I make the Property Title to be displayed as the textboxes text and how do I do this for the - any type of user control containable - contencontrol?
Thanks in advance,
Frank
Maybe DataContext of control or page was not set. - First of all you should read more about a Binding ("http://www.silverlight.net/learn/data-networking/binding/data-binding-to-controls-(silverlight-quickstart)"). If you are working on real project and will design a some arhitecture, you should read about MVVM pattern.
The answer is ElementPropertyBinding. I need to reference the User Control in the Binding or add the binding in the constructor.
Create the binding in XAML:
<User Control ... x:Name="userControl">
...
<TextBlock x:Name="TextBlockHeader" Text="{Binding Title, ElementName=userControl}" />
</UserControl>
Create the binding in the constructor (Code behind)
public MyUserControl()
{
// Required to initialize variables
InitializeComponent();
TextBlockHeader.SetBinding(TextBlock.TextProperty, new System.Windows.Data.Binding() { Source = this, Path = new PropertyPath("Title") });
}
I still need to find out how to add a child control, but that's another question.

Problem with ItemsSource binding in Silverlight ListBox

I'm trying to list some strings in a Silverlight ListBox. I'm binding a vanilla List to the ItemsSource and then specifying the property of the List item to display in DisplayMemberPath. There is something specific to my implementation that causes the ListBox to display the templated items instead of the property specified inside those items.
Here's the scenario. I have a Parent class that derives from UserControl that adds a "Title" Dependency Property. I create a few Child controls that derive from Parent and specify that inherited Title property. For some reason, binding to that Title property in the ListBox causes the unexpected behavior. Here's the code:
public class Parent : UserControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Parent), new PropertyMetadata(String.Empty));
}
The Child XAML code (Child1 and Child2 are basically the same XAML with trivial codebehinds)
<v:Parent x:Class="TemplateBindingTest.Child1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="clr-namespace:TemplateBindingTest"
mc:Ignorable="d" Title="Baby 1" Height="41" Width="94">
<Grid x:Name="LayoutRoot" Background="#FFFFBBBB">
<TextBlock HorizontalAlignment="Center" Text="I da baby" VerticalAlignment="Center" FontSize="14" />
</Grid>
The "ViewModel"
public class TheViewModel : INotifyPropertyChanged
{
public List<Parent> Babies { get; set; }
public TheViewModel()
{
Babies = new List<Parent>();
Child1 baby1 = new Child1();
Child2 baby2 = new Child2();
Babies.Add(baby1);
Babies.Add(baby2);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.xaml
<UserControl x:Class="TemplateBindingTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xmlns:my="clr-namespace:TemplateBindingTest" Height="268" Width="355">
<Grid x:Name="LayoutRoot" Background="White">
<ListBox ItemsSource="{Binding Path=Babies}" DisplayMemberPath="Title" Height="219" HorizontalAlignment="Left" Margin="12,12,0,0" VerticalAlignment="Top" Width="180" />
<my:Child1 HorizontalAlignment="Left" Margin="203,26,0,0" x:Name="child1" VerticalAlignment="Top" />
<my:Child2 HorizontalAlignment="Left" Margin="203,92,0,0" x:Name="child2" VerticalAlignment="Top" />
</Grid>
So ignoring the fact that it's a bit weird to maintain a list of UI controls in a "viewmodel" class, this is all fairly simple Silverlight. In the ListBox control on MainPage I would expect to see the title for each Child control. Instead, the Child controls themselves show up in the ListBox. What am I missing here? I find it very odd that Silverlight just decides to render the Children controls with no complaints in the Debug Output or other error messages. It's like the DisplayMemberPath attribute gets completely ignored. Could this be a bug in Silverlight?
For ease of testing, here's a link to the full Visual Studio project containing the code above.
This behaviour seems to be by design. If the listbox sees that the Content is a derivative of a UIElement then it makes the simple (and I think reasonable) assumption that you intend for that content to be displayed.
You are right what you are doing is "a bit weird" and you are paying the price. This answer may hold a solution for you. However I would recommend you review the choice to hold an instance of a UserControl in the viewmodel.
#AnthonyWJones is correct and the link he provided led to this answer: I would need to implement my own hacked up version of ListBox in order to achieve the desired functionality mentioned in my question.
public class MyListBox : ListBox
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return false;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
((ListBoxItem)element).ContentTemplate = ItemTemplate;
}
}
I guess the moral of this story is don't try to deal with UI elements directly in your ViewModel classes. It's not best practice (and definitely not MVVM) for a reason.

Binding from View-Model to View-Model of a child User Control in Silverlight? 2 sources - 1 target

So i have a UserControl for one of my Views and have another 'child' UserControl inside that.
The outer 'parent' UserControl has a Collection on its View-Model and a Grid control on it to display a list of Items.
I want to place another UserControl inside this UserControl to display a form representing the details of one Item.
The parent UserControl's View-Model already has a property on it to hold the currently selected Item and i would like to bind this to a DependancyProperty on the child UserControl. I would then like to bind that DependancyProperty to a property on the child UserControl's View-Model.
I can then set the DependancyProperty once in XAML with a binding expression and have the child UserControl do all its work in its View-Model like it should.
The code i have looks like this..
Parent UserControl:
<UserControl x:Class="ItemsListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel}">
<!-- Grid Control here... -->
<ItemDetailsView Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel.SelectedItem}" />
</UserControl>
Child UserControl:
<UserControl x:Class="ItemDetailsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel}"
ItemDetailsView.Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel.Item, Mode=TwoWay}">
<!-- Form controls here... -->
</UserControl>
EDIT: Heres how i created the Dependancy Proeprty on the child UC:
public partial class ItemDetailsView : UserControl
{
private static readonly DependencyProperty itemProperty;
static ItemDetailsView()
{
ItemDetailsView.itemProperty = DependencyProperty
.Register("Item", typeof(Item), typeof(ItemDetailsView), null);
}
public Item Item
{
get { return (Item)GetValue(ItemDetailsView.itemProperty); }
set { SetValue(ItemDetailsView.itemProperty, value); }
}
public static Item GetItem(DependencyObject target)
{
return (Item)target.GetValue(itemProperty);
}
public static void SetItem(DependencyObject target, Item value)
{
target.SetValue(itemProperty, value);
}
}
The selected Item is bound to the DependancyProperty fine. However from the DependancyProperty to the child View-Model does not.
It appears to be a situation where there are two concurrent bindings which need to work but with the same target for two sources.
Why won't the second (in the child UserControl) binding work?? Is there a way to acheive the behaviour I'm after??
Cheers.
Well, it looks like you are trying to use a "normal" DependencyProperty on the parent UserControl, and an "attached" DependencyProperty on the child UserControl. You need to pick one way. :)
EDIT for clarification:
There are two ways of registering a dependency property, "Normal", like so:
public static readonly DependencyProperty BobProperty =
DependencyProperty.Register("Bob",....)
and Attached:
public static readonly DependencyProperty BobAttachedProperty =
DependencyProperty.RegisterAttached("BobAttached",...)
Let's say the control you are registering these properties on is called "MyPanel". To use each property:
<MyPanel Bob="somevalue" MyPanel.BobAttached="somevalue"/>
Note the need to specify "where the attached property is defined". Attached properties are great when you have some bit of behavior or functionality that applies to multiple types of controls.
That said, perhaps there is a better way to do this - If the parent UserControl contained an ItemsControl, the ItemTemplate for that control could be a DataTemplate that contained the ItemDetailsView, in which case you could use standard data binding to do what you needed to:
<UserControl blahblahblah>
<ItemsControl ItemsSource="{Binding WhereYourItemsAre}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ns:WhatYourChildViewIsCalled DataContext="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>

Resources