I am trying to use the following code example from the Infragistics site and I'd like edits in the XamDataCards to be reflected in the XamDataGrid. However, my DataSource for the XamDataGrid is an ObservableCollection<Companies> in my ViewModel. How can I also bind to the card and relay updates back to my Companies object in the ViewModel?
<igDP:XamDataGrid x:Name="dgCompanies" Theme="IGTheme" DataSource="{Binding Companies}" SelectedDataItemsScope="RecordsOnly">
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings CellClickAction="SelectCell" AllowEdit="True"/>
</igDP:XamDataGrid.FieldSettings>
</igDP:XamDataGrid>
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding Path=SelectedDataItems, ElementName=dgCompanies}"
Theme="IGTheme">
Edit: Added ViewModel
public class CompanyMgmtViewModel : ViewModelBase
{
private ObservableCollection<Object> _Companies = null;
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
RaisePropertyChanged(GetPropertyName(() => Companies));
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = from res in AODB.Context.TCompanies
select res;
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
The Model/Context code is just EF Database First generated.
You would need to bind your XamDataGrid's SelectedDataItems property to a property of type object[] ie. SelectedCompanies in your ViewModel and bind to that for your XamDataCards' datasource.
The accepted answer in this thread has a sample that shows how to do this, albeit with a ListBox instead of XamDataCards:
http://www.infragistics.com/community/forums/t/89122.aspx
Just replace that ListBox with your XamDataCards control, it works and updates the XamDataGrid. The ViewModel in the example is contained in the MainWindow code-behind, so it is MVVM like you want.
more info:
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/xamDataGrid_Selected_Data_Items.html
IG's SelectedDataItems is an object[] :
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/InfragisticsWPF4.DataPresenter.v14.1~Infragistics.Windows.DataPresenter.DataPresenterBase~SelectedDataItems.html
I couldn't have gotten to this answer without Theodosius' and Ganesh's input - so thanks to them, they both had partial answers.
I first tried to bind the SelectedDataItems of the XamDataGrid to the XamDataCards by way of a property on the ViewModel as Theodosius suggested, but that wasn't enough. Thanks to Ganesh, I implemented INotifyPropertyChanged on my model objects, by inheriting from ObservableObject in MVVMLight (how did I not know the Model needed this?).
Below are the relevant pieces of code to make it work.
I also implemented PropertyChanged.Fody as documented here; that's where the TypedViewModelBase<T> and removal of RaisePropertyChanged() comes from.
I'm also creating my Model objects by using a LINQ/Automapper .Project().To<T>() call which can be found here.
Model
public class Company : ObservableObject
{
public Company() { }
public int id { get; set; }
public string strName { get; set; }
public string strDomicileCode { get; set; }
}
ViewModel
public class CompanyMgmtViewModel : TypedViewModelBase<Company>
{
private ObservableCollection<Object> _Companies = null;
private Object[] _selectedCompany = null;
public Object[] Company
{
get { return _selectedCompany; }
set
{
if (_Company != value)
{
_selectedCompany = value;
}
}
}
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = AODB.Context.TCompanies.Project().To<Company>();
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
View
<igDP:XamDataGrid x:Name="dgCompanies"
Theme="IGTheme"
DataSource="{Binding Companies, Mode=OneWay}"
SelectedDataItemsScope="RecordsOnly"
SelectedDataItems="{Binding Company}">
...
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding ElementName=dgCompanies, Path=SelectedDataItems}"
Theme="IGTheme">
Related
I Follow MVVM model for my application, I have a textbox which acts as an input to filter the collection. I understood observablecollection filter using lambda expression but i couldn't understand collectionviewsource methods. how can i implement collectionviewsource methods using this.
Here is my viewmodel class:
private ObservableCollection<SPFetchCREntity> _CRmappings2 = new ObservableCollection<SPFetchCREntity>();
public ObservableCollection<SPFetchCREntity> CRmappings2
{
get { return _CRmappings2; }
set
{
_CRmappings2 = value;
RaisePropertyChanged("CRmappings2");
}
}
public ICollectionView AllCRSP
{ get; private set;}
public void UpdatePopList()
{
CRmappings2 = new ObservableCollection<SPFetchCREntity>(crentities.Where(p => p.MU_Identifier == selectmu.ToString()).ToList());
}
Bind to the ICollectionView and filter this one:
public void UpdatePopList()
{
CRmappings2 = new ObservableCollection<SPFetchCREntity>(crentities.ToList());
AllCRSP = CollectionViewSource.GetDefaultView(CRmappings2);
AllCRSP.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return entity != null && entity.MU_Identifier == selectmu.ToString();
};
}
private string _selectmu;
public string Selectmu
{
get { return _selectmu; }
set { _selectmu = value; AllCRSP.Refresh(); } //<-- refresh the ICollectionView whenever the selectmu property gets set or when you want to refresh the filter
}
I have a XDocument read from xml file:
public ObservableCollection<Product> GetProducts()
{
ObservableCollection<Product> _products = new ObservableCollection<Product>();
XDocument doc = XDocument.Load(#".\Config\MCU.xml");
foreach (XElement productRow in doc.Root.Elements("MCU"))
{
var m = new Product(productRow.Element("MCUName").Value, Convert.ToUInt32(productRow.Element("MCUNumber").Value), Convert.ToUInt32(productRow.Element("FlashAddress").Value),
Convert.ToUInt32(productRow.Element("PageCount").Value), Convert.ToUInt32(productRow.Element("PageSize").Value), productRow.Element("BinFile").Value,
Convert.ToUInt32(productRow.Element("RAMCodeAdd").Value), Convert.ToUInt32(productRow.Element("MainCR").Value), Convert.ToUInt32(productRow.Element("CRTrimmingAdd").Value),
Convert.ToUInt32(productRow.Element("CRTrimmingLength").Value), Convert.ToUInt32(productRow.Element("UIDAdd").Value), Convert.ToByte(productRow.Element("UIDLength").Value),
productRow.Element("UID").Value, productRow.Element("UserArea").Value);
_products.Add(m);
}
return _products;
}
Now I want to binding XElement MCUName to combobox:
<ComboBox x:Name="cb_MCUType" SelectedItem="{Binding MCUName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
ItemsSouce in the code behind:
public MainWindow()
{
InitializeComponent();
cb_MCUType.ItemsSource = App.ProductDb.GetProducts();
}
But this doesn't work, the combobox populate the Product, how should I fix this? Thanks!
Update:
Thanks for the replies. As you suggested, now I would like to write this in MVVM, so I change my original code:
XAML:
<ComboBox x:Name="cb_MCUType" ItemsSource="{Binding ProductsList}" SelectedValue="{Binding SelectedProduct}" DisplayMemberPath="MCUName" />
ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private ProductDB pd = new ProductDB();
public MainViewModel()
{
DefaultValue_Load();
}
public ObservableCollection<Product> ProductsList { get; set; }
private Product _selectedProduct;
public Product SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
NotifyPropertyChanged("SelectedProduct");
}
}
public void DefaultValue_Load()
{
ProductsList = new ObservableCollectioin<Product>(pd.GetProducts());
}
}
When you create the Products in GetProducts() you provide MCUName as the first parameter in the constructor. For the following sample, I'll assume, that there is a property McuName on every product:
public MainWindow()
{
InitializeComponent();
cb_MCUType.ItemsSource = App.ProductDb.GetProducts().Select(p => p.McuName);
}
It is worth to mention, that this is not a clean MVVM implementation. You should consider to redesign your application to follow the MVVM patter.
I have a combobox, which draws it's items from an ObservableCollection of a custom type using Bindings. I've set the DisplayMemberPath so it displays the correct string and stuff. Now I'm fiddling with the SelectedItem/SelectedValue. It needs to be dependant on the selected item of a ListBox, which is bound to a different ObservableCollection of another custom type, but which has a property of the same type of the ComboBox list.
How can I bind this using MVVM? Is it even possible?
I've got my code here:
MainWindowViewModel.cs
private ObservableCollection<Plugin<IPlugin>> erpPlugins;
public ObservableCollection<Plugin<IPlugin>> ERPPlugins
{
get
{
return erpPlugins;
}
set
{
erpPlugins = value;
OnProprtyChanged();
}
}
private ObservableCollection<Plugin<IPlugin>> shopPlugins;
public ObservableCollection<Plugin<IPlugin>> ShopPlugins
{
get
{
return shopPlugins;
}
set
{
shopPlugins = value;
OnProprtyChanged();
}
}
private ObservableCollection<Connection> connections;
public ObservableCollection<Connection> Connections
{
get {
return connections;
}
set
{
connections = value;
}
}
public MainWindowViewModel()
{
instance = this;
ERPPlugins = new ObservableCollection<Plugin<IPlugin>>(GenericPluginLoader<IPlugin>.LoadPlugins("plugins").Where(x => x.PluginInstance.Info.Type == PluginType.ERP));
ShopPlugins = new ObservableCollection<Plugin<IPlugin>>(GenericPluginLoader<IPlugin>.LoadPlugins("plugins").Where(x => x.PluginInstance.Info.Type == PluginType.SHOP));
Connections = new ObservableCollection<Connection>
{
new Connection("test") { ERP = ERPPlugins[0].PluginInstance, Shop = ShopPlugins[0].PluginInstance } // Debug
};
}
Connection.cs
public class Connection
{
public string ConnectionName { get; set; }
public IPlugin ERP { get; set; }
public IPlugin Shop { get; set; }
public Connection(string connName)
{
ConnectionName = connName;
}
}
And the XAML snippet of my ComboBox:
<ComboBox
Margin="10,77,232,0"
VerticalAlignment="Top"
x:Name="cmbERP"
ItemsSource="{Binding ERPPlugins}"
SelectedItem="{Binding ElementName=lbVerbindungen, Path=SelectedItem.ERP}"
DisplayMemberPath="PluginInstance.Info.Name"
>
Alright, I solved it by changing the IPlugin type in the Connection to Plugin. Why I used IPlugin there in the first place is beyond my knowledge. But like this, I have the same type of Plugin everywhere.
Thanks for your help, appreciate it
i just started learning WPF as i am moving on from WinForm. At the moment i am having difficulties displaying bind data from class to tree view.
My tree view works perfectly if i use .Items.Add() method but when it comes to binding class data to TreeView this is what i see:
Here is the c# code:
public MainWindow()
{
InitializeComponent();
Search sc = new Search();
sc.query(null, "");
this.DataContext = sc;
}
Here is the xaml
<TreeView Width="400" Height="500" Name="TreeViewB" ItemsSource="{Binding getTreeResults}" Style="{StaticResource myTreeView}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Network}">
<TextBlock Text="{Binding getNetwork}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Edited - 2 class added
Here is my class A
class Social_Searcher
{
List<Social_Network> networks = new List<Social_Network>();
public List<Social_Network> getTreeResults { get { return networks; } }
}
Here is my class B
class Social_Network
{
private string network_name;
private List<Keypair> data;
public Social_Network()
{
data = new List<Keypair>();
}
public struct Keypair
{
public void add(string _name, string _value)
{
name = _name;
value = _value;
}
public string name, value;
}
public string Network
{
get { return network_name; }
set { network_name = value; }
}
public void add(string name, string value)
{
if (name == "network")
{
network_name = value;
}
Keypair kp = new Keypair();
kp.add(name, value);
data.Add(kp);
}
public string getNetwork()
{
return network_name;
}
public List<Keypair> getData()
{
return data;
}
public string findKey_value(string key)
{
foreach (Keypair kp in data)
{
if (kp.name == key) return kp.value.ToString();
}
return "null";
}
}
You don't give much code, but getTreeResults and getNetwork look like methods, and your TextBlock will not know how to present them (normally, it would use the results of ToString(), but I don't know if that will work with a method.
If you want those methods, you can try it this way:
public string TreeResults { get { return sc.getTreeResuls(); }}
and then
<TreeView ... ItemsSource={Binding TreeResults} ... > ...
The same goes for getNetwork. I.e., you wrap each method in a public property.
If you don't want to do that, or can't, you can use an IValueConverter
There is clearly something going on in your UI, but it's hard to tell what exactly.
You will likely find a debugging tool such as Snoop useful, as it will allow you to click on items in your UI and see how they exist in the logical tree. You can modify their properties while the program is running to experiment and learn what you need to change in your source code.
I ran into this issue when converting a Windows Forms application to WPF. I know it sounds ridiculous, but make sure that your value is stored in the TreeViewItem's "Header" property, NOT the "Name" property. Once I did this, my list populated as expected.
I want to show a ComboBox with OPTGROUP style header gruopings in Silverlight. Every website I find (including questions on SO) that sovle this link to an outdated link and, handily, show no code snippets for me to work from.
E.g.:
So how do I do this?
See my similar question: How to show group header for items in Silverlight combobox?
I put dummy entries in collection source to indicate the group header and then modified DataTemplate to show the group headers in different way and normal entries in separate way.
Here is one generalized approach. It's not bad, and should be flexible enough -- although it has the weakness of requiring an extended collection class (cannot make use of CollectionViewSource).
Step 1 ComboBoxGroupHeader object
This plays the role of "dummy entry" mentioned by #NiteshChordiya.
public class ComboBoxGroupHeader
{
public ComboBoxGroupHeader(object header)
{
Header = header;
}
public object Header { get; protected set; }
}
Step 2 Extended ComboBox
This overrides PrepareContainerForItemOverride, in order to tinker with the dummy items' containers. It also provides an (optional) "HeaderTemplate".
public class GroupedComboBox : ComboBox
{
public DataTemplate HeaderTemplate
{
get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
}
public static readonly DependencyProperty HeaderTemplateProperty =
DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(GroupedComboBox), new PropertyMetadata(null));
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var container = element as ComboBoxItem;
if (container != null && item is ComboBoxGroupHeader)
{
// prevent selection
container.IsHitTestVisible = false;
// adjust the container to display the header content
container.ContentTemplate = HeaderTemplate
container.Content = ((ComboBoxGroupHeader)item).Header;
}
}
}
Step 3 Extended collection class
This does the grouping, and adds the dummy "ComboBoxGroupHeader" entries. This implementation is essentially read-only (groupings would break if you tried to add new items), but it would be straight-forward to support operations like "Add", "Insert", etc.
public class GroupedCollection<T, TGroup> : ObservableCollection<object>
{
private Func<T, TGroup> _grouping;
public IEnumerable<T> BaseItems
{
get { return base.Items.OfType<T>(); }
}
public GroupedCollection(IEnumerable<T> initial, Func<T, TGroup> grouping)
: base(GetGroupedItems(initial, grouping))
{
_grouping = grouping;
}
private static IEnumerable<object> GetGroupedItems(IEnumerable<T> items, Func<T, TGroup> grouping)
{
return items
.GroupBy(grouping)
.SelectMany(grp =>
new object[] { new ComboBoxGroupHeader(grp.Key) }
.Union(grp.OfType<object>())
);
}
}
Usage
It works like a normal ComboBox, with the optional HeaderTemplate:
<local:GroupedComboBox ItemsSource="{Binding Source}">
<local:GroupedComboBox.HeaderTemplate>
<TextBlock FontSize="9" TextDecorations="Underline" Foreground="DarkGray"
Text="{Binding}" />
</local:GroupedComboBox.HeaderTemplate>
</local:GroupedComboBox>
For the grouped to take effect, the source must use the "GroupedCollection" above, so as to include the dummy header items. Example:
public class Item
{
public string Name { get; set; }
public string Category { get; set; }
public Item(string name, string category)
{
Name = name;
Category = category;
}
}
public class Items : GroupedCollection<Item, string>
{
public Items(IEnumerable<Item> items)
: base(items, item => item.Category) { }
}
public class ViewModel
{
public IEnumerable<Item> Source { get; private set; }
public ViewModel()
{
Source = new Items(new Item[] {
new Item("Apples", "Fruits"),
new Item("Carrots", "Vegetables"),
new Item("Bananas", "Fruits"),
new Item("Lettuce", "Vegetables"),
new Item("Oranges", "Fruits")
});
}
}
Group header is not supported in ComboBox. Alternate way for to implement it with DataGrid or use TreeView to show grouped data.
However, you can try something like this,
Silverlight Custom ComboBox