Tricky binding in WPF - wpf

I have a list of countries and a list of translations in different languages for each country, this is available in my ViewModel as Countries and Translations.
I want to display each country and its translations in ONE datagrid row.
Each country may have different translations filled.
The datagrid I am using allows to add new columns at runtime.
I managed to show this using a multibinding:
foreach (var language in languages)
{
Binding translationsBinding = new Binding { Path = new PropertyPath("Translations") };
Binding languageBinding = new Binding { Source = language.ID };
MultiBinding multiBinding = new MultiBinding();
multiBinding.Converter = new TranslationsMultiConverter();
multiBinding.Mode = BindingMode.TwoWay;
multiBinding.Bindings.Add(translationsBinding);
multiBinding.Bindings.Add(languageBinding);
}
My question is: how can I set a changed translation back to my Translations list, by using a binding only?
Regards

Related

Datagrid is not binding when using ObservableCollection<object>

Hi i am new silverlight application development. i want to bind a data grid from code behind
for that i have
ObservableCollection<object> GridDataSource
As datasource, and i use it as
dgAvailibilityOption.ItemsSource = GridDataSource;
Now despite this data grid not showing any records. For a check if i bind
List<int> testint = new List<int>();
//Add data to list 1 to 10 e.g
dgAvailibilityOption.ItemsSource = testint
then datagrid shows perfect rsults as expected
Now my qiestion is that, is there any problem in taking ObservableCollection type for binding the datagrid??
Convert to List while assigning to datagrid,
Also check you have items inside the collection,
dgAvailibilityOption.ItemsSource = GridDataSource.ToList();
I assume you have, AutoGenerateColumns to be true in GridDataSource.

Convert DataGrid cell content

I have a DataGrid whose ItemsSource is set to a DataTable. The DataTable has a column of type DateTime, and I would like to display informational text (ie. "N/A") if the date in a particular cell is a certain value.
My first thought was to somehow bind the cell content to itself, and use a converter, but I can't seem to get it working correctly, and it seems as though there should be a better way.
Additionally, both the DataGrid and DataTable are dynamically generated, so this has to be done in the code behind.
Here's the code I tried initially:
// Create a new DataGridCellStyle
Style myStyle = new Style();
myStyle.TargetType = typeof(DataGridCell);
// Create the binding
Binding myBinding = new Binding();
myBinding.RelativeSource = RelativeSource.Self;
myBinding.Converter = new DateTimeToStringConverter();
// Add the Content setter
Setter mySetter = new Setter();
mySetter.Property = ContentProperty;
mySetter.Value = myBinding;
myStyle.Setters.Add(setter);
// Set the Style and ItemsSource
myDataGrid.CellStyle = myStyle ;
myDataGrid.ItemsSource = myDataTable.DefaultView;
DateTimeToStringConverter does implement IValueConverter, but I'm guessing the problem lies somewhere with the binding, since DateTimeToStringConverter is never actually called when the DataGrid is displayed.
At first, you Add the variable with the name setter to the Setters collection, but you are define the variable with the name mySetter. It may be a reason, why your Converter is not actually called.
Also the solution for your problem will be a bit more complicated.
Actually the Convert get a value of type RowDataView which contains a data for whole row. There is not an information in the converter about a Column or a Cell that is actually binded.
Better will be skip AutoGenerateColumns and generate them programmatically.
Here is example:
myDataGrid.ItemsSource = myDataTable.DefaultView;
myDataGrid.AutoGenerateColumns = false;
foreach (DataColumn column in myDataTable.Columns)
{
Binding binding = new Binding(column.ColumnName)
{
Converter = new DateTimeToStringConverter()
};
DataGridColumn gridColumn = new DataGridTextColumn
{
SortMemberPath = column.ColumnName,
Header = column.ColumnName,
Binding = binding
};
myDataGrid.Columns.Add(gridColumn);
}
Of course, for a performance will be better use the converter only in a DateTime columns.

Why do widgets update simultaneously when assigned to the same data source?

I have a very basic question about Windows Forms datasources.
If I assign the same object data source to a combobox and a listbox on the same form I observe interesting UI behavior: when I change the item in a combobox (or listbox) the other control selects the same item.
I have no extra code for this UI behavior so I wonder how it works.
var persons = new List<Person>
{
new Person {Id = 1, Age = 10, Name = "Alex"},
new Person {Id = 2, Age = 12, Name = "Boris"},
};
// ListBox
lbPersons.DisplayMember = "Name";
lbPersons.DataSource = persons;
// ComboBox
cbPersons.DisplayMember = "Name";
cbPersons.DataSource = persons;
Please, explain how a control's selected item is changed synchronously?
I have found some similar problems on the web. I don't fully understand how this works, but I will give my best shot at it:
When you have a bind multiple controls to the same datasource, they use the same bindingcontext. Therefore, switching the selected item on one control will change the the selected item on the other control.
Instead when you bind the datasources, give each one a new BindingContext:
lbPersons.DisplayMember = "Name";
lbPersons.DataSource = persons;
lbPersons.BindingContext = new BindingContext();
cbPersons.DisplayMember = "Name";
cbPersons.DataSource = persons;
cbPersons.BindingContext = new BindingContext();
I found this info in a forum (link below) where they confirm the issue and have a solution. I need to do more reading on this, but more info can be found in the msdn at:
http://bytes.com/topic/c-sharp/answers/850851-multiple-controls-bound-same-data-source
http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingcontext.aspx
EDIT as per:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.bindingcontext(v=vs.71).aspx
The BindingContext object of a Control is used to return a single
BindingManagerBase object for all data-bound controls contained by the
Control. The BindingManagerBase object keeps all controls that are
bound to the same data source synchronized. For example, setting the
Position property of the BindingManagerBase specifies the item in the
underlying list that all data-bound controls point to.
Also:
(http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingcontext.bindingcontext(v=vs.71).aspx)
For example, if you have two BindingManagerBase objects (from two
different BindingContext objects), you can set the Position properties
of each BindingManagerBase to different values causing each set of
data-bound controls will display different values from the same data
source.

WPF - Display Grid of Results With Dynamic Columns/Rows

I'm querying an online service (google data feed) which can return results that will have different numbers of columns and rows with each request.
So far I have been unable to get the data grid or grid to work for me. Ideally I want something that works like excel - you can just add rows and set the values for an individual cell
You can create a class e.g. myGridCol that represents the column and create a collection of the columns. Read the google data feed and create the columns. Then you need to add the columns individually e.g. myGridCol[0], myGridCol[1] .. as DataGridColumns in the code behind. You cannot bind directly to a column collection.
You simply bind to a collection for the rows that has a collection for the columns.
In my case I am using a GridView but I have used the same approach with DataGrid
In my case sDocs is an ObservableCollection
sDoc has public List DocFields
The Fields collection is exactly the same in each sDoc because I made sure it was.
If the Fields collection is not the same in each sDoc then it does not like that.
sDocs is the ItemsSource for the GridView
Then in the code behind I add the columns. As I said before you cannot bind directly to a columns collection. Notice you can even bind the Path to a Property (.e.g. DispValueShort). My class for DocField has other Properties and methods. DocField is actually an Abstract Class with and Abstract Property DispValueShort. Then I have classes for string, date, and enumeration that implement DocField because edit of a string is different from edit of a date. I even have classes for single value and multi value. This is a stable production application.
Binding
<ListView Grid.Row="1" Grid.Column="0" x:Name="lvSrchResulsGrid"
ItemsSource="{Binding Path=MyGabeLib.Search.SDocs}"
Code behind
sDocBaseResultDocsFieldsIndex = 0;
foreach (GabeLib.DocField docField in sDocBaseResultDocsFields)
{
// Debug.WriteLine(" sDocBaseResultDocsFields DispName = " + docField.FieldDef.DispName);
if (fd.FieldDef == docField.FieldDefApplied.FieldDef)
{
gvc = new GridViewColumn();
gvch = new GridViewColumnHeader();
gvch.Content = fd.FieldDef.DispName;
gvch.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
if (fd.FieldDef.Sort)
{
gvch.Click += new RoutedEventHandler(SortClick);
gvch.Tag = fd.FieldDef.Name;
}
if (!fd.AppliedDispGrid) gvc.Width = 0; // how to hide
gvc.Header = gvch;
gvBinding = new Binding();
gvBinding.Mode = BindingMode.OneWay;
gvBinding.Path = new PropertyPath("DocFields[" + sDocBaseResultDocsFieldsIndex.ToString() + "].DispValueShort");
template = new DataTemplate();
textblock = new FrameworkElementFactory(typeof(TextBlock));
textblock.SetValue(TextBlock.TextProperty, gvBinding);
textblock.SetValue(TextBlock.TextTrimmingProperty, TextTrimming.WordEllipsis);
// <Setter Property="TextTrimming" Value="WordEllipsis" />
template.VisualTree = new FrameworkElementFactory(typeof(Grid));
template.VisualTree.AppendChild(textblock);
gvc.CellTemplate = template;
gvSearchResults.Columns.Add(gvc);
break;
}
sDocBaseResultDocsFieldsIndex++;
}

DataTemplate in listview

I use a listview to display data like a data matrix (columns and rows). My problem is : my items are typed : MatrixCellVM.
I tried everything I found on the net to apply a DataTemplate on this items but nothing worked.
Here is the latest technique I'm using
foreach (var col in dataMatrix.Columns)
{
//create the data template
DataTemplate cellLayout = new DataTemplate();
cellLayout.DataType = typeof(MatrixCellVM);
//set up the stack panel
FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(Grid));
spFactory.Name = "myComboFactory";
//set up value textblock
FrameworkElementFactory cellValue = new FrameworkElementFactory(typeof(TextBlock));
cellValue.SetBinding(TextBlock.TextProperty, new Binding("Value"));
cellValue.SetValue(TextBlock.ToolTipProperty, "Value");
spFactory.AppendChild(cellValue);
//set the visual tree of the data template
cellLayout.VisualTree = spFactory;
gridView.Columns.Add(new GridViewColumn{
Header = col.Name,
DisplayMemberBinding = new Binding(string.Format("[{0}]", count)),
CellTemplate = cellLayout
});
count++;
}
One more thing, when I override ToString() in MatrixCellVM, the value is displayed (but with this technique I add a context menu or give any color to my value).
I solved the problem but have another one.
So what I did is pretty simple. DisplayMemberBinding and CellTemplate can't be put together.
So I deleted the whole c# code creating the datatemplate.
The only thing that remains is :
gridView.Columns.Add(
new GridViewColumn
{
Header = col.Name,
CellTemplate = (DataTemplate)listView.Resources["MatrixCellVMTemplate"]
});
Thus, I added in my xaml code defining the datatemplate.
My new problem is that my rows are arrays of MatrixCellVM (as it is a matrix, I don't have properties and I don't know by advance how many columns I will have).
That was the point of the DisplayMemberBinding = new Binding(string.Format("[{0}]",count)) code in my top post.
How could I reproduce this behavior in my datatemplate (what is the binding in my textblock).
If anything is not clear, let me know.
Thanks

Resources