Different ItemsSource to each user control - wpf

In my window I have ListBox that contain collection of usercontrols like:
<ListBox ItemsSource="{Binding UserControls}">
Inside the user control class I have DataGrid that bind to datatable:
<DataGrid ItemsSource="{Binding TableResult}"/>
From the window's viewmodel I'm adding dynamiclly more usercontrols to the ListBox, like:
void AddUserControl()
{
MyUserControl uc = new MyUserControl
{
DataContext = this
};
UserControls.Add(uc);
}
For now all the usercontrols bind to the same table: TableResult
But what I what is a new DataTable for each usercontrol.
I have tried like:
void AddUserControl()
{
MyUserControl uc = new MyUserControl
{
DataContext = this
};
DataTable dt = new DataTable();
uc.MyGridView.ItemsSource = dt.DefaultView;
UserControls.Add(uc);
}
but now this part will be missing for the new datatable:
DataTable dt;
public DataTable Dt
{
get { return dt; }
set
{
dt = value;
RaisePropertyChanged("Dt");
}
}
Note: I have only one viewmodel for the window, therefore the datacontext of each usercontrol is the same viewmodel
Any suggestions for this secnario? I mean how do I bind a new datatable for each new usercontrol

You shouldn't bind to an IEnumerable<UserControl>. Referencing UI elements or controls in a view model breaks the MVVM pattern.
What you should do is to create a class to which you add the DataTable property and then bind to an IEnumerable<YourClass>. The actual UserControl should be defined in the ItemTemplate of the ListBox in the view:
<ListBox ItemsSource="{Binding DataObjects}">
<ListBox.ItemTemplate>
<local:MyUserControl />
</ListBox.ItemTemplate>
</ListBox>
The root element in the ItemTemplate inherits its DataContext from the current item in the ListBox which means that you can bind directly to any public property of YourClass in MyUserControl:
<DataGrid ItemsSource="{Binding TableProperty}"/>

Related

WPF binding - DataGrid.Items.Count

in my View, there are a DataGrid and a TextBox, which is bound to the DataGrid's Items.Count property:
<DataGrid x:Name="dataGrid" ItemsSource="{Binding dataTable}"/>
<TextBox Text="{Binding Items.Count,ElementName=dataGrid,Mode=OneWay,StringFormat={}{0:#}}"/>
The ViewModel has a property (e.g. ItemsCount) which I'd like to be bound to the Items.Count property of the DataGrid, but have no idea, how to achieve this.
class ViewModel : INotifyPropertyChanged
{
public DataTable dataTable {get;set;}
public int ItemsCount {get;set;}
}
Maybe I could also use the Rows.Count property of the DataTable the DataGrid is bound to, but how would i bind or link the two properties in the ViewModel?
So I basically want the ItemsCount property to be synchronized with the dataTable.Rows.Count property.
A common way to achieve your requirements are to declare properties to data bind to the UI controls:
<DataGrid x:Name="dataGrid" ItemsSource="{Binding Items}" />
<TextBox Text="{Binding ItemsCount}" />
...
// You need to implement the INotifyPropertyChanged interface properly here
private ObservableCollection<YourDataType> items = new ObservableCollection<YourDataType>();
public ObservableCollection<YourDataType> Items
{
get { return items; }
set { items = value; NotifyPropertyChanged("Items"); NotifyPropertyChanged("ItemCount"); }
}
public string ItemCount
{
get { Items.Count.ToString("{0:#}"); }
}
UPDATE >>>
As #Sivasubramanian has added his own requirement to your question, in case you need to update the item count specifically by adding to your collection, you can manually call the NotifyPropertyChanged method:
Items.Add(new YourDataType());
NotifyPropertyChanged("ItemCount");

WPF DataGrid binding to DataTable in XAML

I'm completely new to WPF/XAML. I'm trying to work out XAML code to bind a DataTable to DataGrid. What I have is an instance of custom DataContainer class which implements INotifyPropertyChanged. This class has a property:
private DataTable totalsStatus = new DataTable();
public DataTable TotalsStatus
{
get { return totalsStatus; }
set
{
totalsStatus = value;
NotifyPropertyChanged("TotalsStatus");
}
}
now, in the C'tor of my MainWindow I have this, which works like a charm:
Binding b = new Binding();
b.Source = DataContainer;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("TotalsStatus");
DataGridMain.SetBinding(DataGrid.ItemsSourceProperty, b);
How do I make this binding in XAML?
You need to use an objectdataprovider.
<ObjectDataProvider x:Key="yourdataproviderclass"
ObjectType="{x:Type local:yourdataproviderclass}" />
<ObjectDataProvider x:Key="dtable"
ObjectInstance="{StaticResource yourdataproviderclass}"
MethodName="GetTable"/> <!--here would be the method that returns your datasource-->
Then you can bind it to your datagrid in XAML with
<DataGrid ItemsSource="{Binding Source={StaticResource dtable}}" ></DataGrid>
There are different ways to do bindings in xaml though, so play around with it a bit.

How i set a DataTable to be the ItemsSource of a DataGridComboBoxColum?

I have a DataTable with two Columns that i will bind to all the ComboBoxes of a DataGridComboBoxColumn where one of the Columns will be the Text of the Items, and other will be the Values of the Items.
I know that the property called 'DisplayMemberPath' is where i specify the Name of the Column to be the Text of the Items, and to the values it have to be specified in the 'SelectedValuePath' property.
But one more time it's a problem to Bind whatever to a DataGridComboBoxColumn because it can't access the DataContext of the DataGrid.
So how i set a DataTable to be the ItemsSource of a DataGridComboBoxColum?
Example where what i want works in Code Behind:
TestClass test = new TestClass();
dataGrid.Columns.Add(new DataGridComboBoxColumn()
{
Header = "City",
DisplayMemberPath = "Cities",
SelectedValuePath = "ID",
ItemsSource = test.Dt.DefaultView,
});
Here is my XAML Code:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp3
Title="MainWindow" Height="350" Width="600">
<Grid Name="grid1">
<DataGrid Name="dataGrid" AutoGenerateColumns="False" MinColumnWidth="100">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="City" DisplayMemberPath="Cities" SelectedValuePath="ID" ItemsSource="{Binding local:TestClass.Dt}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Here is my Code Behind code:
public class TestClass
{
public TestClass()
{
(...)
//Here i am loading my DataTable
}
private static DataTable dt;
public static DataTable Dt
{
get { return dt; }
}
}
Either,
A. explicity set the DataContext of ComboBox to your DataTable, OR
B. if DataTable is some part of the object that you have bound to the DataGrid, use RelativeSource in ComboBox binding.
You will need to post your code, if you need specific help for these scenarios.

WPF ObservableCollection in xaml

I have created an ObservableCollection in the code behind of a user control. It is created when the window loads:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Entities db = new Entities();
ObservableCollection<Image> _imageCollection =
new ObservableCollection<Image>();
IEnumerable<library> libraryQuery =
from c in db.ElectricalLibraries
select c;
foreach (ElectricalLibrary c in libraryQuery)
{
Image finalImage = new Image();
finalImage.Width = 80;
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri(c.url);
logo.EndInit();
finalImage.Source = logo;
_imageCollection.Add(finalImage);
}
}
I need to get the ObservableCollection of images which are created based on the url saved in a database. But I need a ListView or other ItemsControl to bind to it in XAML file like this:
But I can't figure it out how to pass the ObservableCollection to the ItemsSource of that control. I tried to create a class and then create an instance of a class in xaml file but it did not work. Should I create a static resource somehow>
Any help will be greatly appreciated.
Firstly, the ObservableCollection is a local variable. What you need to do is have it as a private global variable and expose it with a public property. You can use the INotifyPropertyChanged interface to have the image data update automagically when the actual collection itself changes.
In your XAML, you then need to set the DataContext to self, and you can then directly bind your public property to the ItemsSource. You may want to use an ItemTemplate for displaying the items in a custom manner.
Cheers,
Adam
Example as requested:
In C#:
public MyWindowClass
{
public ObservableCollection<image> MyImageCollection
{
get;
set;
}
}
In XAML:
<UserControl
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
...
<ListBox ItemsSource="{Binding MyImageCollection}" ItemTemplate="*yourtemplateresource*" />
...
</UserControl>
Now, the reason that I mentioned using INotifyPropertyChanged is that if you try:
MyImageCollection = new ObservableCollection<image>();
The items in the listbox will not automatically update. With an ObservableCollection, however, you do not need to implement INotifyPropertyChanged for basic addition and removal of list items.
You have to set the DataContext of the UserControl to your collection:
DataContext = _imageCollection
You can do that in the UserControl_Loaded() method.
Next you need to bind the ItemsSource of the ListView in the XAML:
<ListView ItemsSource="{Binding}"/>
The {Binding} is equivalent to {Binding .} which binds to the DataContext of the UserControl. If you need "more stuff" in your DataContext you can instead create a class like this:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
...
}
Use this class for the DataContext:
DataContext = new ViewModel();
And replace the binding to bind to the Images property:
<ListView ItemsSource="{Binding Images}"/>
Then you can add another property to ViewModel:
class ViewModel : INotifyPropertyChanged {
public ObservableCollection Images { get { ... } }
public String Message { get { ... } set { ... } }
...
}
And bind it to a control:
<TextBlock Text="{Binding Message}"/>
Remember to fire the PropertyChanged event when the Message property is changed in ViewModel. This will update the UI when view-model properties are changed by code.

CheckedItems property for custom CheckBoxList control in Silverlight

I need to implement CheckBoxList control with ItemsSource and CheckedItems properties. Items from ItemsSource should be displayed as checked checkboxes if CheckedItems contains these values or unchecked otherwise. Also I need two-way databinding support for CheckedItems property (value of this property should be updated when user clicks on checkboxes).
Here some code which probably can help to understand my problem
XAML:
<UserControl x:Class="Namespace.Controls.CheckBoxList" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox x:Name="LayoutRoot">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Code behind:
public partial class CheckBoxList : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CheckBoxList), null);
public static readonly DependencyProperty CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof(IEnumerable), typeof(CheckBoxList), null);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public IEnumerable CheckedItems
{
get { return (IEnumerable)GetValue(CheckedItemsProperty); }
set { SetValue(CheckedItemsProperty, value); }
}
public CheckBoxList()
{
InitializeComponent();
LayoutRoot.SetBinding(ItemsControl.ItemsSourceProperty, new Binding("ItemsSource") { Source = this });
}
}
I think that I need to bind ListBox to UserControl with custom converter, which will return collection of items with additional IsChecked property, but it works only in case of one-way data binding.
Looks like I need two-way binding to two properties at one time, but I don't know how to implement it and will appreciate any help with this issue.
Thanks in advance.
First of all you should consider deriving from ListBox rather than UserControl. The ListBox already does most of what you want.
Secondly consider one way binding to an IList. You can then add and remove entires to that IList as the respective items are selected.
Rather than try to bind a CheckBox control in an Item Template you make a copy of the ListBox styles, place them in Generic.xaml as the style of your new control. Then modify the unselected and selected visual states using a checked and unchecked check box as part of the visual appearance.
Now you can attach to the SelectionChanged event and use the Event args AddedItems list to add to the bound IList and the RemovedItems list to remove items from the bound list.
You would need to clear and re-add the set of items to the list box SelectedItems list when either your CheckedItems is assigned or the ItemsSource is changed.
There are probably a number gotchas that you will need to work round but this seems like a more direct path to your goal than starting from scratch with a UserControl base.
Add an observable collection for your list box datasource to your datacontext:
private ObservableCollection<MyItem> _myItems;
public ObservableCollection<MyItem> MyItems
{
get { return _searchByFields; }
set
{
_myItems = value;
}
}
Add a class to hold the data about your checkboxes:
public class MyItem
{
public bool Checked {get; set; }
public string MyItemValue { set ; set; }
}
Then in your data template bind listbox to the collection and your data template checkboxes to the respective MyItem properties:
<UserControl x:Class="Namespace.Controls.CheckBoxList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox x:Name="LayoutRoot"
DataContext="[Dataconext here]"
ItemsSource={Binding MyItems}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}"
Content="{Binding MyItemValue}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Don't forget to set the DataContext of the binding to the appropriate class (you might be doing this in the XAML or the code behind perhaps)

Resources