WPF: Binding items added to UserControl's exposed children - wpf

I have a user control that allows items to be added to it by exposing a Grid's Children property. Any control I add shows up fine but when I try to bind a property on the added item to a control in the main window nothing happens (example):
<TextBox Name="txtTest" Text="Success!" />
<mycontrols:CustomUserControl.ExposedGridChildren>
<TextBox Text="{Binding ElementName=txtTest, Path=Text, FallbackValue=fail}"/>
</mycontrols:CustomUserControl.ExposedGridChildren>
This example always results in the TextBox's text showing "fail". Here is how I'm exposing the children in the user control:
public UIElementCollection ExposedGridChildren
{
get { return grdContainer.Children; }
}
Any thoughts? Is it a scope issue? I know I can't name the elements I add to the children because of scope errors. Thanks, Brian.

This might answer your question:
How do you bind a grid's children to a list?
Or as Dr. WPF puts it: (http://drwpf.com/blog/2007/10/15/itemscontrol-a-is-for-abundance/)
Is a Panel an ItemsControl?
No. The logical children of a panel
are UIElements, whereas the logical
children of an ItemsControl (its
Items) can be any CLR objects.
Sidebar: So what is a panel? The main
role of a panel is to provide layout
support for its children. Although a
panel does maintain a collection of
child elements, it is not technically
a WPF “control”… That is, it does not
derive from the Control base class and
it does not support the WPF notion of
templating. Instead, it is a single
element with a single purpose… namely,
to size and position (a.k.a., measure
and arrange) its children.

I was apparently going about this thing all wrong. What I needed to do was create a look-less control and template it to have one (or more) contentpresenter(s).

Related

Combobox Text property not changing in user control

I have a list box that has a list of user controls. Each user control has 5 combo boxes. I want to be able to read the selected text of each combo box in each user control from the main application. However, when I change the selection in the combo box, the text property of the combo box in the user control doesn't change when I read it in the main application.
Code-behind:
radQueryParamList.Items.Add(new TCardQueryParameters());
Xaml (This is just a data template for how to display a TCardQueryParameters object):
<DataTemplate x:Key="TCardViewQueryParamDataTemplate">
<tcardqueryparam:TCardQueryParameters x:Name="TCardViewerParamUC" />
</DataTemplate>
<telerik:RadListBox Grid.Column="1" ItemTemplate="{StaticResource TCardViewQueryParamDataTemplate}" Name="radQueryParamList" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.ColumnSpan="3">
Where I loop over the list of user controls
string test = radTESTGACC.Text;//TEST combo box, Text property changes
//radQueryParamList is a listbox of user controls where TCardQueryParameters is the UC
foreach(TCardQueryParameters param in radQueryParamList.Items)
{
//Each UC has a radGACC combo box in it, and I am reading what the user
//selected for each user control here in the main app, but the text property
//never changes
String gacc = param.radGACC.Text; //Text property DOESN'T CHANGE
}
I thought that each instance of the user control would keep its own state and I would just be able to read what the user selected for that combo box, but that doesn't seem to be the case.
You have not bound the SelectedItem, SelectedValue, or SelectedIndex property of your internal ComboBox to anything so it maintains its selection.
An ItemTemplate is like a cookie cutter. It contains the definition of the object, but not the object itself. Properties specific to the object's state are lost unless they are bound to something on the DataContext behind the template.
This is important to note for two aspects.
First off, to improve performance WPF usually unloads items which are not visible, which often results in items being re-created from their template anytime they are reloaded. An example would be when you minimize an application to the taskbar, then maximize it again. This is usually better on performance and memory usage, however it does mean you have to be sure you store the state of items that were created with a Template somewhere.
And second, by default ListBoxes use something called virtualization. A simple way of explaining this would be this:
Suppose you have a ListBox of 100,000 items. In your ListBox, only 10 items can be visible at a time. WPF will render roughly 14 items (the 10 visible ones, and then a few extra for a scroll buffer so you don't see anything unusual while scrolling). When you scroll to new items, WPF just re-uses the existing items that are already rendered, and just replaces the DataContext behind those items.
As you can guess, it is far better on performance to render 14 UI items instead of 100k items.
So to answer your question, you will probably want to bind either SelectedItem, SelectedValue, or SelectedIndex of your TCardQueryParameters UserControl to a property on the DataContext (which in your case appears to be another different UserControl).
It should probably be noted that what you are essentially doing is creating a list of UserControls, assigning them to the ListBox, and then telling the ListBox that it should draw each UserControl with another separate UserControl. So although you are changing the SelectedItem in the template UserControl, that change is not being reflected to your ListBox.Items copy of the UserControl.
I'm guessing you probably don't want that, so would recommend removing your ItemTemplate completely.
Or better yet, create a new class object containing all the data for your UserControl, and adding that to your ListBox.Items. Then tell the ListBox to draw that item using a TCardQueryParameters UserControl as the ItemTemplate, like you have now.

WPF databinding in child control

I am new to WPF. I'm trying to build an application which has a function (call it Initialisation) where a user has to fill in a lot of data and some parts of the form are repeated. We're rewriting a legacy app that has quite a long wizard in although we will probably use collapsible panels in one window rather than next/previous pages. Also some parts are repeated e.g. the user can specify a number of items, if they say 3 they will need to fill in some configuration info for each, so those controls would need to be repeated three times.
I'm using MVVM and am using this example here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
The old wizard had about 4 pages so I'm intending to have one user control (Initialisation) that contains 4 child user controls to break the xaml up a bit.
So far I have the Initialisation (its ViewModel inherits from Workspace ViewModel as in the above example) and it contains one child which is working:
<Expander ExpandDirection="Down" Header="ChildOne">
<view:ChildOne />
</Expander>
I will have separate ViewModels for each child and for Intialisation and this brings me to my problem.
The problem I am having is that ChildOne contains a dropdown which I am trying to bind like so:
<ComboBox x:Name="textMessageTypeCmb" ItemsSource="{Binding Path=TextMessageSelectionOptions, Mode=OneTime}"/>
TextMessageSelectionOptions is a public property in ChildOne's ViewModel. This results in no errors but an empty dropdown - that property getter is never called. If I move that property getter code into the Initialisation's ViewModel instead it works but I'm trying to keep my code in manageable chunks so I'd like to put hat code back in ChildOne's ViewModel. It also works if in my MainWindow I create ChildOne as a workspace instead of Initialisation like this
ChildOneViewModel ws = this.Workspaces.FirstOrDefault(vm => vm is ChildOneViewModel) as ChildOneViewModel;
Can anyone advise whether I am taking the right approach (by dividing it up into several user controls) and what I need to do in the binding to make this work? I don't really understand any of this yet especially binding.
It seems to me that your ChildOne view's DataContext is still this Initialisation vm.
You can bind it the views Datacontext to a ChildOneViewModel object
...
<view:ChildOne DataContext={Binding PropertyReturnsChildOneViewModellObject/>
...
or specify the path for the combobox ItemsSource prop.
<ComboBox x:Name="textMessageTypeCmb" ItemsSource="{Binding Path=PropertyReturnsChildOneViewModellObject.TextMessageSelectionOptions, Mode=OneTime}"/>
Note: PropertyReturnsChildOneViewModellObject is a property of the Initialisation vm.

Dynamicly putting Object to many visual parents in WPF

I want to create my own usercontrol which will take some FrameworkElement as parameter (as Content) and then it will didplay it on few copies (how much? it depends) Anyway copies has to be binded to their VM. Single copy will be probably a StackPanel with few binded buttons and TextBoxes. So it will look like this:
<MyControl> <Panel with stuff in it/> </MyControl>
Now in my control I hot ItemsControl and I am dynamicly putting there items. But one content (stack panel for example) can only has one parent so it doesn't work. How can I achive this? I heard that DataTemplates can help me but I don't know how. Also I wander if I can't do something like this:
<MyControl> <ViewModel of Panel with stuff/> </MyControl>
You need to take a DataTemplate property that contains the FrameworkElement(s), then create ContentPresenters in the control, with their Content set to the piece of ViewModel and their ContentTemplate set to your DataTemplate property.

UserControl child controls and FindName

I have a Silverlight UserControl which uses the ContentPropertyAttribute to exposes the Children property of one of it's child panels. This allows me to add child controls to the panel on my page:
<local:MyUserControl>
<TextBox Name="tbTest" />
</local:MyUserControl>
This works, apart from the 'tbTest' field of the page being present, but not initialised. On closer inspection, the InitializeComponent method does try to locate the TextBox (with FindName), but fails to do so (returning null).
After some investigation, I've found that namescopes are the problem - the UserControl has it's own namescope, thus it's children can't be located with the page's FindName but can with the UserControl's FindName method.
How can I alter my UserControl so that the child controls are locatable by the InitializeComponent method? The standard Panels (StackPanel, Grid, etc.) don't seem to have any problem doing so, so there must be a solution?
Thanks
It may be difficult to do at this point but the best course of action would probably be to derive your control from ItemsControl instead of UserControl. Then you wouldn't have the problem with name scopes.
I suppose as a workaround you could do a dive into the control with VisualTreeHelper to manually set the tbTest field.

How to bind StackPanel Children from a ViewModel?

New to Silverlight. I'm working on a chat application where new chat messages are added to the bottom of a list. I had a working version that used as StackPanel inside a ScrollViewer and then in some code behind used StackPanel.Children.Add().
I'm trying to convert this to a View-ViewModel approach, and I can't figure out how to bind the Children of the StackPanel to any collection property. I've tried this:
<ScrollViewer Name="scrollMessages" Grid.Row="2" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel x:Name="pnlMessages" Orientation="Vertical" Children="{Binding Path=ExampleTBs}" />
</ScrollViewer>
where ExampleTBs is a collection of TextBlocks created in code. This fails XAML parsing, the Children property isn't bindable in this way.
Is the approach of binding to the StackPanel itself fixable? Should I be using a different container type? I saw another question where the guy created the entire StackPanel in code and then used a ContentPresenter...
Bottom line, I'd like to find a way to databind my view to a viewmodel using something like a StackPanel as a container where successive items will be added to the container over time. Best approach?
Use a ListBox (or any other ItemsControl) and bind the ItemsSource property to an ObservableCollection in your ViewModel.
Do you need to use a StackPanel? If you use an ItemsControl instead, this still presents each chat message in a vertical list, and also allows for binding of the data.

Resources