Setting separate data binding for controls in User Control - wpf

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}">
.............

Related

View inside a View and data synchronization

I am having one view where I need to display some Grid and TabControl. There is one column on a grid that should display something like a Note (Remark) property. Since this field can contain large amount of data, I am going to have one tab with TextBox control that should allow user to see/edit note, while grid column will show only a few first letters on the note.
I am going to post relevant parts only:
public classSomeViewModel : ViewModelBase
{
public SomeViewModel()
{
TabScreens = New List<ViewModelBase>();
TabScreens.Add(new AnotherViewModel1());
TabScreens.Add(new AnotherViewModel2());
}
List<ViewModelBase> TabScreens{get;set;}
}
SomeView xaml:
<DataTemplate DataType="{x:Type vm:AnotherViewModel1}">
<vw:AnotherView1 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:AnotherViewModel2}">
<vw:AnotherView2 />
</DataTemplate>
AnotherView2:
<Grid>
<TextBox Text={Binding Note} />
</Grid>
AnotherViewModel2:
public class AnotherViewModel2
{
public string Note {get;set;}
}
}
So TabControl on View is bound to TabScreens. DataTemplates ensure that both AnotherView1 and AnotherView2 will be loaded when SomeView is loaded. Each row in grid contains different remark. What is the cleanest way to synchronize SomeViewModel Remark and AnotherViewModel2 Remark?
Combine the 2 view models into a master view model with a single Remark property. Don't introduce a need to synchronize if it's not necessary.

WPF MVVM dynamic collection of user controls in a wrap panel

I'm new to WPF but have created a window with a wrap panel that contains a collection of user control instances dynamically added from code behind. Each user control will ultimately display data from a row returned from a database call. I'd like to make this follow MVVM but am a little stuck on the architecture. I think I need to have a view model for the user control and a view model for the window that would possess an observablecollection of the user control view models. How do I get that bound to a wrap panel on the view side so that the wrap panel sees the collection of user control view models and knows to establish a user control for each instance in the collection?
I think once this is all bound properly I can make a background worker that at a regular interval queries the database and creates / updates the user control view model objects and if I am inheriting from INotifyPropertyChanged and firing property changed events in my user control view model everything should update based on the binding. Does that sound correct?
I've seen basic examples such as an observablecollection of strings bound to a list box but I'm having trouble applying this to a more complex case. Any suggestions as to a general architecture or where I should look to get started is much appreciated!
Basically, you need an enumerable control that has one item for each element in your ObservableCollection. The control's items will need to be templated to display the data using your custom control
To do this create an ObservableCollection which holds your data objects and use it as the ItemsSource for a ListBox. The ListBox will then need to be changed to display its items in a WrapPanel instead of the default layout. Modify the ItemTemplate of the ListBox to use your custom user control for each list item.
ViewModel:
public class WindowViewModel
{
public ObservableCollection<MyDatabaseObject> DatabaseObjects { get; set; }
}
public class MyDatabaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string DbName
{
get { return _dbName; }
set {
_dbName = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("dbName");
}
}
private _dbName;
}
Xaml:
<Grid>
<ListBox ItemsSource="{Binding DatabaseObjects}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<MyUserControl Title="{Binding DbName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Are you looking for the ItemsControl class? With ItemsControl.ItemTemplate set to a DataTemplate for your ItemUserControlViewModel (-> item user control view). And ItemsControl.ItemsPanel set to anItemsPanelTemplate with a WrapPanel.
ItemControl's ItemsSource property would be bound to your ObservableCollection<ItemUserControlViewModel> from WindowViewModel.

Access the child control of a user control

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)
{
....

What techniques can I employ to create a series of UI Elements from a collection of objects using WPF?

I'm new to WPF and before I dive in solving a problem in completely the wrong way I was wondering if WPF is clever enough to handle something for me.
Imagine I have a collection containing objects. Each object is of the same known type and has two parameters. Name (a string) and Picked (a boolean).
The collection will be populated at run time.
I would like to build up a UI element at run time that will represent this collection as a series of checkboxes. I want the Picked parameter of any given object in the collection updated if the user changes the selected state of the checkbox.
To me, the answer is simple. I iterate accross the collection and create a new checkbox for each object, dynamically wiring up a ValueChanged event to capture when Picked should be changed.
It has occured to me, however, that I may be able to harness some unknown feature of WPF to do this better (or "properly"). For example, could data binding be employed here?
I would be very interested in anyone's thoughts.
Thanks,
E
FootNote: The structure of the collection can be changed completely to better fit any chosen solution but ultimately I will always start from, and end with, some list of string and boolean pairs.
I would strongly recommend the ItemsControl, its behaviour is as close as you can get to the ASP.Net repeater control so it is very flexible.
Declare the item control as:
<ItemsControl Name="YourItemsControl"
ItemsSource="{Binding Path=YourCollection}"
ItemTemplate="{StaticResource YourTemplate}">
</ItemsControl>
Then you can use the datatemplate to organise the data into a display format for the user
<DataTemplate x:Key="ProjectsTemplate">
<StackPanel Margin="0,0,0,10">
<Border CornerRadius="2,2,0,0" Background="{StaticResource ItemGradient}" d:LayoutOverrides="Width, Height">
<local:ItemContentsUserControl Height="30"/>
</Border>
...
Useful ItemsControl Links
http://drwpf.com/blog/itemscontrol-a-to-z/
http://www.galasoft.ch/mydotnet/articles/article-2007041201.aspx
I hope this helps you.
You can use Data Templates. Here's a good post about it.
This is exactly the kind of scenario WPF simplifies. Event-handlers- bah! Data-binding and data templates make this a cinch. I have constructed an example illustrating how you can do this.
Here is the code-behind, which declares a class to represent your items- PickedItem. I then create a collection of these items and populate it with some samples.
public partial class DataBoundCollection : Window
{
public DataBoundCollection()
{
Items = new ObservableCollection<PickedItem>();
Items.Add(new PickedItem("Item 1"));
Items.Add(new PickedItem("Item 2"));
Items.Add(new PickedItem("Item 3"));
InitializeComponent();
}
public ObservableCollection<PickedItem> Items
{
get;
set;
}
}
public class PickedItem
{
public PickedItem(string name)
{
Name = name;
Picked = false;
}
public string Name
{
get;
set;
}
public bool Picked
{
get;
set;
}
}
Now, let's look at the XAML mark-up for this window:
<Window x:Class="TestWpfApplication.DataBoundCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBoundCollection" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Picked}" Margin="5"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I create a ListBox to hold the items, and bind its ItemsSource property to the collection I created in the code-behind. Then, I provide the ListBox with an ItemTemplate, which determines how each PickedItem will be rendered. The DataTemplate in this case is as simple as a check-box and some text, both bound to the member variables on PickedItem. Now, when I check any of these items, the data in the underlying collection is modified, in real-time, with no event handlers needed. Ta-da!
alt text http://img405.imageshack.us/img405/1083/databoundcollection.png

How to bind an observable collection to Multiple user controls at runtime?

I am stucked at the part where I have to bind a collection to a dynamic usercontrol. Scenario is something like this.
I have a dynamic control, having a expander , datagrid, combobox and textbox, where combox and textbox are inside datagrid. There are already two collections with them. One is binded with combobox and another is binded with datagrid. When the item is changes in combox its respective value is set to its respective textbox, and so on. and this pair of value is then set to the collection binded with datagrid. A user can add multiple items.
Now the main problem is that all these things are happening inside a user control which is added dynamically, that is on button click event. A user can add desired numbers of user controls to the form.
problem is coming in this situtaion. Say I have added 3 controls. Now in 1st one if i add a code to the collection then it gets reflected in the next two controls too, as they are binded with same collection.
So, I want to know is there anyway to regenrate/rename the same collection so that the above condition should not arise.
It's hard to answer your question without seeing the bigger picture, however I have a feeling you are going about this the wrong way. It appears that you are adding instances of your user control directly from code. Instead of doing that, you should create some kind of ItemsControl in your XAML, and in its ItemTemplate have your user control. Bind that ItemsControl to a collection in your view model, and only manipulate that collection.
You should not be referring to visual controls in your view model or code behind. Whenever you find yourself referencing visual elements directly from code, it should raise a warning flag in your mind "Hey! There's a better way than that!"...
Example:
The view model:
public class ViewModel
{
public ObservableCollection<MyDataObject> MyDataObjects { get; set; }
public ViewModel()
{
MyDataObjects = new ObservableCollection<MyDataObject>
{
new MyDataObject { Name="Name1", Value="Value1" },
new MyDataObject { Name="Name2", Value="Value2" }
};
}
}
public class MyDataObject
{
public string Name { get; set; }
public string Value { get; set; }
}
The window XAML fragment containing the list box and the data template:
<Window.Resources>
...
<DataTemplate x:Key="MyDataTemplate">
<local:MyUserControl/>
</DataTemplate>
</Window.Resources>
...
<ListBox ItemsSource="{Binding MyDataObjects}"
ItemTemplate="{StaticResource MyDataTemplate}"
HorizontalContentAlignment="Stretch"/>
The user control:
<UniformGrid Rows="1">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Value}" HorizontalAlignment="Right"/>
</UniformGrid>

Resources