Wpf Binding filteration - wpf

I have a doubt binding a textbox.he scenario is like this.I hava a dataset say,
DataTable dt=new DataTable();
dt.TableName = "table";
dt.Columns.Add("mode", typeof(int));
dt.Columns.Add("value", typeof(int));
DataRow dr = dt.NewRow();
dr["mode"] = 1;
dr["value"] = 1000;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["mode"] = 2;
dr["value"] = 2000;
dt.Rows.Add(dr);
DataSet ds = new DataSet();
ds.Tables.Add(dt);
this.DataContext = ds;
The window is bound to this dataset.I have textbox in my window and i want to bind it to the row with mode=1, so that i can show that rows value in the text property of my textbox.
How can i apply this binding..?
Any input will be highly helpfull

DataSets are a bit generic to be using for binding in WPF. Its usually easier to use the M-V-VM pattern where you have models that are INotifyPropertyChanged or DependencyObjects that your UI binds against.
I'm not sure if you're talking about changing what things are bound to depending on the "mode" or if you just want to filter on "mode."
In the first case, you'd have to use a DataTrigger on a Style in order to change the ContentTemplate that you're using based on the value of your mode field. This is NOT an easy concept for the beginner or intermediate user.
This is a decent blog post with instructions on how to accomplish this. Again, its pretty confusing and when it doesn't work its sometimes hard to troubleshoot.
In the second case, you'd be better served by setting your DataContext to a type that contains multiple DataTables that are pre-filtered. Filtering isn't a job for the UI, its a job for code. It might look something like:
public class MyDataContext
{
public DataTable ModeOne {get;set;}
public DataTable ModeTwo {get;set;}
}
or perhaps
public class MyDataContext
{
public Dictionary<int, DataTable> TableByMode {get;set;}
}
where you would bind like this
<ItemsControl Content="{Binding TableByMode[1]}">

Related

WPF: Refreshing DataGrid after columns changed in DataTable, MVVM way

I am using vanilla WPF Datagrid that has its ItemsSource bound to a DataTable:
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding ResultTable.DefaultView}" >
Where ResultTable is the DataTable. I have tried adding rows programmatically at runtime and the DataGrid will update accordingly. However, the DataGrid does not update When I add or remove columns at runtime. Here is what I have in my ViewModel
class MyViewModel : ObservableObject
{
private DataTable resultTable;
public DataTable ResultTable
{
get { return resultTable; }
set
{
resultTable = value;
RaisePropertyChanged("ResultTable");
}
}
public void AddColumn(string columnName)
{
ResultTable.Columns.Add(columnName);
}
}
I found an almost identical question here WPF Datagrid using MVVM.. is two way binding to DataTable possible? but there did not seem to be a conclusive answer. Unfortunately, the person who asked the question seemed to have found a workaround but did not bother to post it...
I also found a solution here http://www.mikeware.com/2012/08/datagrid-dilemma/ but it appears very "hackish" (not to mention non-MVVM) and the author himself admits that he would prefer to do it another way if he found one.
How can I force the DataGrid to update when I add new columns? I prefer to do it in a MVVM way if possible.
First add this code to ViewModel:
private static readonly DataTable _dt = new DataTable();
Then you can add that what like this code when you add column:
public void AddColumn(string columnName)
{
var temp = this.ResultTable;
this.ResultTable = _dt;
temp.Columns.Add(columnName);
this.ResultTable = temp;
}

WPF Bind control to DataView

I am having a LOT of trouble trying to bind my controls to a data source. I tried binding to XML document. That worked, but lots of issues when I tried to refresh the XML document itself and have it update the UI.
My newest try is to bind my controls to a DataView, which seems simple. I have a sample app I found here on StackOverflow, which does this:
public MainWindow()
{
InitializeComponent();
DataTable dataTable = GetTable();
Binding dataTableBinding = new Binding();
dataTableBinding.Source = dataTable;
dataTableBinding.Path = new PropertyPath("Rows[0][MyTextColumn]");
txtMyTextColumnDataTable.SetBinding(TextBox.TextProperty, dataTableBinding);
DataView dataView = dataTable.DefaultView;
Binding dataViewBinding = new Binding();
dataViewBinding.Source = dataView;
dataViewBinding.Path = new PropertyPath("[0][MyTextColumn]");
txtMyTextColumnDataView.SetBinding(TextBox.TextProperty, dataViewBinding);
}
This works perfectly, right out of the box. I added a button whose code updates the value in the data table, and the textbox immediately reflects the new value when I click that button.
I tried this in my VB.Net project, like this:
dim plcData As DataTable = GetTable()
dim plcView As DataView = plcData.DefaultView
dim plcBinding As Binding = New Binding
plcBinding.Source = plcView
plcBinding.Path = New PropertyPath("(0)(conveyor_plc_data_Main_FeedCarousel_caroAngle)")
Me.tb.SetBinding(TextBlock.TextProperty, plcBinding)
And it doesn't work. It will not update my UI control.
In both cases, GetTable builds a 1-row DataTable with sample data. In my VB project, tb is a TextBlock on my MainWindow.
In the VB project, I can interrupt my code and query the particular data column in the Immediate window, and the proper value is there. It just won't update into my control.
This seems like a very simple thing to do. I am quite new to WPF, and can't see what is wrong with my code. Eventually I would like to define the binding in my XAML, but can't figure out how to do this. At this point, a code-behind setting of the binding would be ok. I will have many controls to be bound to many data columns.
Can anybody tell me what obvious thing I'm missing here?
According to the documentation, the syntax for the PropertyPath class only accepts C#-style indexers.
Single Indexer on the Immediate Object as Data Context:
<Binding Path="[key]" .../>
The class has no way to change its syntax based on the calling language.
EDIT
To set the binding in XAML when the DataView is created in the code-behind, expose the view as a property:
public static readonly DependencyProperty plcViewProperty
= DependencyProperty.Register("plcView", typeof(System.Data.DataView),
typeof(MainWindow), new PropertyMetadata(null));
public System.Data.DataView plcView
{
get { return (System.Data.DataView)GetValue(plcViewProperty); }
set { SetValue(plcViewProperty, value); }
}
private void MainWindow_Initialized(object sender, EventArgs eventArgs)
{
plcView = GetTable().DefaultView;
}
Then in your XAML:
<Window x:Name="TheWindow" ...>
...
Text="{Binding ElementName=TheWindow,
Path=plcView[0][conveyor_plc_data_Main_FeedCarousel_caroAngle]}"

WPF DataGrid bound to an ObservableCollection that is updated on separate thread fails to maintain sort

Download Sample Project
I have a wpf 4 datagrid that is bound to an ObservableCollection. This ObservableCollection is
updated on another thread. I can see the updates coming through to my simple gui just fine. I can even sort the data. But the sort does not "stick". It will sort once when you click the column header but when a value in the collection changes the sort does not change accordingly.
The MainWindow backing code is where most of the action goes down (just for simplicity of the example). I create an ObservableCollection and pass it to a thread that does the actual writes to the ObservableCollection. I then bind that same ObservableCollection to the datagrid via a CollectionView (I've tried binding it directly as well). My hunch is that the sorting depends upon the collectionChanged event which I'm pretty sure won't fire back to the Dispatcher ( see: http://bea.stollnitz.com/blog/?p=34).
What to do?
public partial class MainWindow : Window
{
private Thread _dataThread;
private Thread _marketThread;
private SampleData _sampleData;
private Market _market;
private ObservableCollection<Stock> stocks;
private ConcurrentQueue<Stock> _updates = new ConcurrentQueue<Stock>();
public MainWindow()
{
InitializeComponent();
stocks = new ObservableCollection<Stock>();
for (var i = 0; i < 5; i++)
{
var newStock = new Stock();
newStock.Id = (uint)i;
stocks.Add(newStock);
}
var source = CollectionViewSource.GetDefaultView(stocks);
dataGrid.ItemsSource = source;
_sampleData = new SampleData(_updates);
_dataThread = new Thread(_sampleData.CreateData) { Name = "Data Thread" };
_dataThread.Start();
_market = new Market(_updates, stocks);
_marketThread = new Thread(_market.Start){Name = "Market Thread"};
_marketThread.Start();
}
}
Challenge.
Download Sample Project
Have you looked at ObjectDataProvider IsAsynchonous="True" and bind in XAML? You might be able to not thread the collection creation. I have no experience how DataGrid sorts behave behind IsAsynchonous="True".
<ObjectDataProvider IsAsynchonous="True" ...>

WPF DataGrid Row add in codebehind

I am from VB.Net WinForms comming. Now I wanted to write a small app in WPF, listing some files in a datagridview. I used WPF's DataGrid, created some Columns. And then failed to add my rows.
Please, can you help me to select the right way to get my filenames, state-text and thumbnails added to the DataGrid Row?
In VB.Net WinForms I can add a row like this:
Datagridview1.Rows.add(Myvalue, "RowStateText", "Hello World", MyDate)
In WPF's DataGrid I can add
DataGrid1.Items.Add(New DataGridRow())
But how to fill my DataGridRow?
Private Sub AddFilesAndFolders(ByVal Base As IO.DirectoryInfo, ByRef dgv As DataGrid)
'For Each di As IO.DirectoryInfo In Base.GetDirectories
' Call AddFilesAndFolders(di, dgv)
'Next
Dim item As DataGridRow
For Each fi As IO.FileInfo In Base.GetFiles
item = New DataGridRow'<-- test 1 (row is added but empty)
Dim di As New MyFileInfo'<-- test 2 (my own class with public members, but how to add as row with declared columns?)
di.FileName = fi.FullName
di.FileDate = fi.LastAccessTime
item.Item = fi.FullName
dgv.Items.Add(di)
Next
End Sub
Hi: you should set an ItemsSource instead of adding items manually. If the columns are set up correctly then it will just 'work'!
dbv.ItemsSource = Base.GetFiles
or
dbv.ItemsSource = CreateMyFileInfos(Base.GetFiles)
If you have any more problems, please post back here.
Edit: on second inspection it looks like you may want to be doing it recursively. In which case your AddFilesAndFolders could instead be CreateFilesAndFolders, which would return a collection of FileInfo/MyFileInfo objects, merged with the collections produced by the child folders recursively; then bind the whole list returned from the first call, to the grid.
Hope that helps!
WPF is a mindset change, you need to get away from the Winforms way of thinking.
Ultimately you need to set the ItemsSource to an IEnumerable, preferably a ObservableCollection.
The quickest way to get started would be to put the ObservableCollection as a public property in your code-behind file:
public ObservableCollection<DirectoryInfo> files { get;set; }
Then in the constructor or a Load event on the Window, populate the collection with your data and then add to the Xaml declaration for your DataGrid:
ItemsSource = "{Binding Path=files}"
EDIT:
I tried this out using the DirectoryInfo class, in my code behind I added:
public ObservableCollection<DirectoryInfo> Dir = new ObservableCollection<DirectoryInfo>();
public Window1()
{
InitializeComponent();
Dir.Add(new DirectoryInfo("c:\\"));
Dir.Add(new DirectoryInfo("c:\\temp\\"));
dataGrid1.ItemsSource = Dir;
}
For some reason this was not working using the Databinding via Xaml, but I did not try very hard to get it to work.

Inserting a new object into L2S table and databinding to it prior to SubmitChanges() in WPF

I'm just getting started with Linq-to-SQL and data binding in WPF, most of which works like a dream so far!
I've got (what I though was) a common scenario:
a) Query list of records from a table via datacontext and bind to the current user control
this.DataContext = db.ClientTypes;
b) Have the user see a bound ListView and some bound detail controls to make changes to the existing records, with a db.SubmitChanges(ConflictMode.FailOnFirstConflict); to push the changes back to the DB. No problem.
c) User wants to add a new record, so we:
ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);
However at this point I dont want to call db.SubmitChanges as I want the user to be able to update the properties of the object (and even back out of the operation entirely), but I want them to be able to see the new record in the bound ListView control. Thinking I just needed to re-run the query:
ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);
// Rebind the WPF list?
this.DataContext = db.ClientTypes;
listView1.SelectedItem = ct;
listView1.ScrollIntoView(ct);
However this doesn't work, the newly created record is not part of the returned list. I'm not sure if this is because of caching within L2S or if I'm just going about this the wrong way. Is there a better way to accomplish this?
Thanks.
Instead of setting your Control.DataContext = db.ClientTypes, store db.ClientTypes somewhere else and bind to an ObservableCollection that wraps it.
var somewhereElse = db.ClientTypes;
var toBind = new ObservableCollection<ClientType>(somewhereElse);
toBind.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
types.InsertAllOnSubmit<AddressType>(e.NewItems.Cast<AddressType>());
};
this.DataContext = toBind;
Then, when the user wants to add a new item:
ObservableCollection<ClientType> toBind = this.DataContext as ObservableCollection<ClientType>;
System.Diagnostics.Debug.Assert(toBind != null);
ClientType ct = new ClientType();
ct.Description = "<new client type>";
toBind.Add((ct);
Calling toBind.Add will cause the CollectionChanged event handler above to call InsertOnSubmit on the original Table instance, so you can call SubmitChanges() when convenient. Obviously, you'd probably want to do the same with Remove ...
Hope that helps :)
It may be worth looking into the MVVM pattern. In MVVM you have a ViewModel which wraps your Model, so you would have a ClientTypeViewModel class.
public class ClientTypeViewModel : INotifyProperyChanged
{
public ClientTypeViewModel(ClientType dataModel)
{
this.dataModel = dataModel;
}
public string Description
{
get { return this.dataModel.Description; }
set
{
this.dataModel.Description = value;
// Raise PropertyChanged event
}
}
private ClientType dataModel;
}
And something like an ApplicationView model, which would contain an ObservableCollection of ClientTypeViewModels.
public ApplicationViewModel
{
public ObservableCollection<ClientTypeViewModel> ClientTypes { get; private set; }
}
You then bind to ApplicationViewModel.ClientTypes instead of the plain data model. This way, your view will be automatically updated whenever a new item is added to ClientTypes, or a property is changed on the ClientType view model. ApplicationViewModel can listen for changes on the ClientTypes collection and automatically add newly added items to the DataContext.
You may think it's overkill for your application, I don't know - but MVVM is definitely somthing worth learning. If it feels like you're struglling or fighting with WPF, MVVM is likely where to look ;)
Look at CreateBindingList.
I think it's just because you're assigning the same reference to the DataContext. Hence, WPF doesn't see the need to refresh the binding. The easiest way around this is to:
// rebind
this.DataContext = null;
this.DataContext = db.ClientTypes;

Resources