DataBinding DataGrid to Custom Data Structure - wpf

So I am just joining the WPF club after dabbling in Winforms for a couple of years. I am definiately enjoing the databinding in WPF, but I am having trouble with the DataGrid.
I have a highly-customized data structure that stores results from a SQL query, which I would like to continue to use in WPF. I have seen examples of binding to lists of objects, but not for anything dynamic like a SQL query.
Is there some interface that I can make my data structure implement so the DataGrid can get the necessary information such as the row & column count, headers, and the data?

the items should implement INotifyPropertyChanged and you should put them in a ObservableCollection<>, so that the grid will be notified about changes.
if you set AutoGenerateColumns to true, then the Grid will automatically create a column for each property of your Model.
if you just have "raw" data and no Model-Class, then i think you have to create the columns manually. WPF bindings are very flexible, it is also possible to bind to indexed properties / arrays etc. Maybe this helps you with "dynamic" data.
<TextBlock Text="{Binding myArray[0]}" />

My goal was to bind to my custom structure similar to a DataTable, allowing the DataGrid to "discover" information about my structure.
The short answer is to implement the following interfaces:
IList
IEnumerable
ITypedList
As well as inheriting from CustomTypeDescriptor (or implementing ICustomTypeDescriptor)
This Question helped a lot in dealing with the CustomTypeDescriptor stuff which was the most challenging.
My enumeration object was simple an object that stored a reference back to the main table so it could ask what the value was at a specific row and column.
public AgilityTableRow(AgilityTableBase table, int rowIndex)
{
_table = table;
_rowIndex = rowIndex;
}
public object this[int columnIndex]
{
get
{
return _table[columnIndex, _rowIndex];
}
}
public object this[string columnName]
{
get
{
return _table[_table.GetFieldIndex(columnName), _rowIndex];
}
}
Note: this class needs to implement ICustomTypeDescriptor and can simply forward the call to the table to get the properties:
public PropertyDescriptorCollection GetProperties()
{
return _table.GetProperties();
}

Related

How to get DataGrid.SelectedIndex from another ".cs" file?(MVVM)

Earlier when I wanted to edit a row in a DataGrid then I just set the cursor on the row of a DataGtid and wrote such code in a method of a codebehind form(xxx.xaml.cs):
int k = XXXDataGrid.SelectedIndex;
and then I could retrieve data from a row of a DataGrid.
Now I try to use MVVM pattern of WPF and I have read that all my CRUD operations should pe written in Models. OKAY. I tried to take "DataGrid.SelectedIndex" but it is not possible without creating a handler in codebehind file.
I would like to know how can I take "DataGrid.SelectedIndex" and data of a row of a DataGrid from other classes situated in "Models" of MVVM.
When are value types stored in stack?
I have read a lot of books of C# and always when I read about values and references types then to my mind comes a question: When are value types stored in stack? Cause programmer cannot initializes any value type out from class.
Your ViewModel will have Properties that are populated Model objects. Here you will assign them so the View will be able to display data.
public ParentModel
{
get { return parentModel; }
private set
{
if (parentModel != value)
{
parentModel = value;
RaisePropertyChanged("ParentModel");
}
}
}
public int SelectedItemIndex
{
get { return selectedItemIndex; }
set
{
if (selectedItemIndex != value)
{
selectedItemIndex = value;
RaisePropertyChanged("SelectedItemIndex");
}
}
}
The View will contain the object to display data, be it DataGrid, ComboBox etc. You can use ItemTemplates to customize how the data is displayed, but the key is to bind your data to the control. This can allow data to flow in either the direction mode of only ViewModel to View (OneWay), or View to ViewModel (OneWayToSource), or both (TwoWay). So as the user changes the selected index, the data will flow back to the ViewModel as it is set for TwoWay below. The ItemsSource here is set as OneWay so only the ViewModel can change that for the View.
<DataGrid ItemsSource="{Binding Path=ParentModel.Items, Mode=OneWay}"
SelectedIndex="{Binding Path=SelectedItemIndex, Mode=TwoWay}" />
If ViewModels need to interact with BusinessLogic, just pass the Data. If a seperate ViewModel needs the information, you'll need to use the concept available in your framework, eg. EventAggregator (Prism), to pass data around since they won't have knowledge of each other.
Core concept of MVVM is the binding of models and WPF controls' properties like this. I think you'll want to read up more on it to fully utilize the power and design. Some very helpful questions/answers can be found on StackOverflow, MSDN has several good tutorials, etc.

Sorting on Templated Column with converters

My datamodel is like this:
public class ModelA
{
public int ModelId{get;set;}
}
public class ModelB
{
public IEnumerable<ModelA> ChildObjects{get;set;}
}
Now in the Xaml, am using a DataGrid with the ItemSource as List(), and have a template column which binds to ChildObjects with a converter doing the job of getting the first element from ChildObjects and returning the value as that object's ModelId. Now all works fine till now. The issue is when I do sorting on this templated column.
I know one workaround is to have an extra property in ModelB which does the job of what converter is doing and make the sortmemberpath in xaml as that new property name, but that is not what I want as its against the model.
Is there any other perfect way to handle this scenario, as the SortMemberPath can't be made as expression as its just a contant.
You've tagged this MVVM, which I assume means your models are actually view models (or are at least wrapped by view models). That being the case, why wouldn't you add the extra property? After all, it's there to support the view. Your view needs the extra property, so your view model should provide it.

Why does System.Data.DataTable work as an ItemsSource, and how can I write my own class that does the same?

I'm writing a fancy business-logic class that's essentially a table with an unknown number of columns, constructed dynamically, which includes various methods for validation, data processing etc.
It's an IEnumerable of specialized row objects, which allow access to their columns through a BusinessData this[string columnName] { get { ... } } accessor. Almost like a DataTable.
I'd like to be able to data-bind it to a WPF DataGrid, so I want to write the following:
dataGrid1.ItemsSource = myFancyMemoryTable;
But, of course, it won't work, because the DataGrid will bind itself to the public properties of my row objects, and not to the columns that contain the business data. How do I get the behavior I want from it?
I cannot specifically track down where the properties are retrieved for DataTables/DataRows but if you implement ITypedList on your source collection that should work.

Implementing INotifyPropertyChanged with ObservableCollection

I want to pull data from a database to display into a ComboBox, and then allow users to select values from that ComboBox and add them into a ListBox (via add/remove buttons). Would I be able to get away with using an ObservableCollections to hold the database values to bind to the ComboBox, since it implements INotifyPropertyChanged (and CollectionChanged)? Sorry if this is a basic question, I starting learning WPF about a month ago.
I've read over the article (very well done) by Sacha Barber.
And I've looked over the MSDN page on ObservableCollection.
What would be the advantages/disadvantages of using an ObservableCollection vs a List (which I know does not implement INotifyPropertyChanged)?
Something you may want to note.
Don't confuse the ObservableCollection's implementation of INotifyPropertyChanged with the objects it contain's implementation.
If one of the properties of one of the objects within the ObservableCollection changes, the UI will not reflect it unless that object implements INotifyPropertyChanged as well. Do not expect the ObservableCollection to take care of this for you.
If the items in your combobox don't change (i.e. you don't add/remove/update items), then List will probably be OK for your needs (ObservableCollection will be too) if you manually notify that your List property changed when you affect it.
public List<X> MyList
{
get
{
...
}
set
{
if (... != value)
{
... = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("MyList"));
}
}
}
}
....
this.MyList = new List<X> { new X(...), new X(...) };
If you plan to add/remove or update items in your combobox (without creating a new MyList object, i.e. using this.MyList.Add(...)), then use ObservableCollection that is able to notify when the collection is updated (so it can update bindings).

User controls communicating via Commands - how?

I'm working on my first project in WPF/XAML, and there's a lot I've not figured out.
My problem is simple - I need a window that has a bunch of fields at the top, with which the user will enter his selection criteria, a retrieve button, and a data grid. When the user clicks on the button, a query is run, and the results are used to populate the grid.
Now the simple and obvious and wrong way to implement this is to have a single module containing a single window, and have everything contained within it - entry fields, data grid, the works. That kind of mangling of responsibilities makes for an unmaintainable mess.
So what I have is a window that is responsible for little more than layout, that contains two user controls - a criteria control that contains the entry fields and the retrieve button, and a data display control that contains the data grid.
The question is how to get the two talking to each other.
Years back, I would have added a function pointer to the criteria control. The window would have set it to point to a function in the display control, and when the button was clicked, it would have called into the display control, passing the selection criteria.
More recently, I would have added an event to the criteria control. I would have had the window set a handler in the display control to listen to the event, and when the button was clicked, it would have raised the event.
Both of these mechanisms would work, in WPF. But neither is very XAMLish. It looks to me like WPF has provided the ICommand interface specifically to accommodate these kinds of connection issues, but I've not yet really figured out how they are intended to work. And none of the examples I've seen seem to fit my simple scenario.
Can anyone give me some advice on how to fit ICommand to this problem? Or direct me to a decent explanation online?
Thanks!
MVVM is the prevalent pattern used with WPF and Silverlight development. You should have a read up on it.
Essentially, you would have a view model that exposes a command to perform the search. That same view model would also expose properties for each of your criteria fields. The view(s) would then bind to the various properties on the view model:
<TextBox Text="{Binding NameCriteria}"/>
...
<Button Command="{Binding SearchCommand}".../>
...
<DataGrid ItemsSource="{Binding Results}"/>
Where your view model would look something like:
public class MyViewModel : ViewModel
{
private readonly ICommand searchCommand;
private string nameCriteria;
public MyViewModel()
{
this.searchCommand = new DelegateCommand(this.OnSearch, this.CanSearch);
}
public ICommand SearchCommand
{
get { return this.searchCommand; }
}
public string NameCriteria
{
get { return this.nameCriteria; }
set
{
if (this.nameCriteria != value)
{
this.nameCriteria = value;
this.OnPropertyChanged(() => this.NameCriteria);
}
}
}
private void OnSearch()
{
// search logic, do in background with BackgroundWorker or TPL, then set Results property when done (omitted for brevity)
}
private bool CanSearch()
{
// whatever pre-conditions to searching you want here
return !string.IsEmpty(this.NameCriteria);
}
}

Resources