WPF Binding using a property as value to define a Binding - wpf

I have to say I am not getting used to WPF.
I have the problem that I have a Property that contains the value, but I want not to bind to that property.
I want to bind the property to a property named as the value in the property Name.
This is my xaml:
<telerik:RadMultiColumnComboBox x:Name="radMultiColumnComboBox_Part"
Grid.ColumnSpan="3" Grid.Row="1"
SelectedItem="{Binding SelectedItem, ElementName=selector, Mode=TwoWay}"
>
<telerik:RadMultiColumnComboBox.ItemsSourceProvider>
<telerik:GridViewItemsSourceProvider ItemsSource="{Binding ItemsSource, ElementName=selector}"
AutoGenerateColumns="False"
>
<telerik:GridViewItemsSourceProvider.Columns>
<!-- This line below is what I am talking about -->
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name, ElementName=selector}" />
</telerik:GridViewItemsSourceProvider.Columns>
</telerik:GridViewItemsSourceProvider>
</telerik:RadMultiColumnComboBox.ItemsSourceProvider>
</telerik:RadMultiColumnComboBox>
I think mostly a try can explain it best:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var sourceProvider = radMultiColumnComboBox_Part.ItemsSourceProvider as GridViewItemsSourceProvider;
var columns = sourceProvider.Columns;
foreach(var gridViewColumn in sourceProvider.Columns)
{
var binding = new Binding(DisplayMemberPath);
binding.Source = gridViewColumn.DataContext;
// there is no property DataMemberBinding.
BindingOperations.SetBinding(gridViewColumn, Telerik.Windows.Controls.GridViewColumn.DataContextProperty, binding);
}
}
I would prefer a solution in xaml but in c# would also be fine.
Thank you.

This is not possible to do in pure XAML. You'll need to create the binding programmatically. Something like this:
var gridViewColumn = sourceProvider.Columns[0] as GridViewDataColumn;
string propertyToBindTo = selector.Name;
gridViewColumn.DataMemberBinding = new Binding(propertyToBindTo);

The solution:
var provider = this.GridViewItemsSourceProvider;
// var provider = this.radMultiColumnComboBox_Part.ItemsSourceProvider as GridViewItemsSourceProvider;
provider.Columns.Add(new GridViewDataColumn() { DataMemberBinding = new Binding("Name") });

Related

How to bind an ItemsControl to a dependency property of a usercontrol?

Trying to understand this better.
I have an ItemsControl defined in my mainview something like this
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
ItemTemplate="{Binding Source={StaticResource MyParagraph}}"
>
</ItemsControl>
in which I would like to use a DataTemplate:
<UserControl.Resources>
<DataTemplate x:Key="MyParagraph">
<v:InkRichTextView
RichText="{Binding ?????? "
</DataTemplate>
</UserControl.Resources>
The InkRichTextView is a view with a dependency property, RichText, being used to pass a paragraph from the ObservableCollection(InkRichViewModel) Notes in the mainview to the user control. That is, this works correctly for one paragragh:
<v:InkRichTextView RichText ="{Binding Path=Note}" Grid.Column="0" Grid.Row="0" />
where Note is defined as a paragraph in the MainView.
The problem is, how do I write the DataTemplate and the ItemsControl such that the ItemsControl can pass each paragraph from the observablecollection to the dependency property RichText in the InkRichTextView?
Thanks for any guidance.
(I hope this is understandable!)
Items control:
<ItemsControl x:Name="NotesItemsControl" Grid.Column="2" HorizontalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<local:InkRichTextView RichText="{Binding Note}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Code behind:
class InkRichViewModel : System.ComponentModel.INotifyPropertyChanged
{
#region Note (INotifyPropertyChanged Property)
private string _note;
public string Note
{
get { return _note; }
set
{
if (_note != value)
{
_note = value;
RaisePropertyChanged("Note");
}
}
}
#endregion
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string p)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(p));
}
}
}
public MainWindow()
{
InitializeComponent();
var item01 = new InkRichViewModel() { Note = "Note 01", };
var item02 = new InkRichViewModel() { Note = "Note 02", };
var item03 = new InkRichViewModel() { Note = "Note 03", };
var item04 = new InkRichViewModel() { Note = "Note 04", };
var item05 = new InkRichViewModel() { Note = "Note 05", };
var itemList = new List<InkRichViewModel>()
{
item01, item02, item03, item04, item05,
};
NotesItemsControl.ItemsSource = itemList;
}
How it looks at runtime:
Is that what you're looking for?
Based on what you describe, it seems that each item in your ItemsControl is a paragraph, the very object you want to assign to the InkRichTextView.RichText property. Is that correct?
If so, keep in mind that within the item template, the data context is the collection item itself - thus, the path you are looking for does not refer to a property of the data context, but to the data context itself.
That is done with the dot (.) path:
<v:InkRichTextView RichText="{Binding .}"/>
I'm posting this as an answer, although the credit goes to O.R.Mapper and Murven for pointing me in the right direction. My post is to help anyone else just learning this.
In very simple terms, the ItemControl performs a looping action over the collection in its ItemsSource. In my case the ItemsSource is a collection of type InkRichViewModel. (Hence the question from Murven). In its looping action, the ItemsSource will create objects from the InkRichViewModel. (Thus, my usercontrol now has an individual datacontext!) Each of these objects will use the ItemTemplate for display. So to simplify things, I moved the DataTemplate from the UserControl Resources to within the ItemControl itself as:
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<v:InkRichTextView RichText="{Binding Note}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now that each of my usercontrols has its own datacontext being assigned by the ItemsControl, the Output window (VS2010) now shows the binding errors. Fixing these errors leads to a working solution.
Hope this helps other newbies like myself. Thanks everyone.
(Ooops! Just saw the answer from Murven but I'll leave this if it helps somebody to understand.)

WPF: Binding DataGrid to collection programmatically

I already spent hours on this, and similar topics did not help. :(
I've got an object of type "Chart" which contains a List "LineItems".
I want to bind LineItems programmatically to a DataGrid in an UserControl.
Usercontrol XAML:
<DataGrid Name="myData" AutoGenerateColumns="True">
Usercontrol Code behind:
public void SetItemSource(ChartingBase.Chart chart)
{
//DataGrid.ItemsSource = chart.LineItems; // working!
// this is not working:
this.DataContext = chart;
Binding b = new Binding( "LineItems" );
b.Mode = BindingMode.TwoWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myData.SetBinding( DataGrid.ItemsSourceProperty, b );
}
Setting just the ItemsSource works. Creating the binding manually does not work and I have no clue what else I could try. Thanks!
Try
BindingOperations.SetBinding(myData, DataGrid.ItemsSourceProperty, new Binding("LineItems") { Source = chart });
In WPF, it is customary to put your data into an ObservableCollection<T> collection and data bind that to the DataGrid.ItemsSource property. Then you can fill or manipulate the collection in code and the UI will update automatically. Try this:
<DataGrid Name="myData" ItemsSource="{Binding Items}" AutoGenerateColumns="True">
...
public void SetItemSource(ChartingBase.Chart chart)
{
this.DataContext = chart;
Items = new ObservableCollection<YourDataType>();
foreach (SomeDataType dataType in chart.SomeCollection)
{
Items.Add(new YourDataType(dataType.SomeProperty, ...));
}
}

Databinding - In XAML, how to databind to a property of the control created dynamically?

The control I created dynamically is a radiobutton, and I am trying to control the visibility of a hyperlinkbutton according to the IsChecked property of the radiobutton created in code-behind.
In my XAML file:
<HyperlinkButton Visibility="{Binding IsChecked, ElementName=tempRadio, Converter={StaticResource visibilityConvert}}" Content="Insert Record" Click="addRecord" Background="Aqua" Foreground="White"></HyperlinkButton>
Apparently I don't think I should use ElementName in this case, since it is only for controls created in XAML.
In my C# file:
public RadioButton tempRadio;
...
I would start with this:
first set the binding target on your hyperlink
hyperlinkButton.BindingTarget = tempRadio.IsChecked;
then set the binding:
hyperlinkButton.SetBinding(hyperlinkButton.BindingTarget, CreateValueBinding());
private Binding CreateValueBinding()
{
var valueBinding = new Binding();
valueBinding.Mode = BindingMode.TwoWay;
valueBinding.NotifyOnValidationError = true;
valueBinding.ValidatesOnExceptions = true;
valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
return valueBinding;
}

I need to loop through rows and check checkbox in a datagrid in silverlight

Hi I am trying to loop through rows in a datagrid
If I use:
PagedCollectionView pgView = dataGrid.ItemsSource as PagedCollectionView;
foreach (var item in pgView.)
{}
I get the item as entity and I can't figure out how to cast that to some meaningfull data
can anyone help me there ?
if I use:
IEnumerable list = dataGrid.ItemsSource as IEnumerable;
foreach (var row in list)
{}
I get the same entity and the same problem...
I have looked at the following that accomplishes the task but I feel like I am
mixing data with presentation
PagedCollectionView pgView = verkefniDataGrid.ItemsSource as PagedCollectionView;
foreach (var item in pgView)
{
((CheckBox)verkefniDataGrid.CurrentColumn.GetCellContent(item)).IsChecked = true;
}
Are the any way to get to the data behind the item and set it there to true ?
You need to iterate through the DataGrid's rows, not through the items it is bound to, if you want to check a checkbox control. You would need to loop through the rows and then grab the checkbox in the proper column and set the properties on it.
But as mentioned in the comments, if the checkbox is bound to a property in the data behind the grid, then you should just be able to change that value (as long as the item exposes the INotifyPropertyChanged interface).
Edit Updated link
Using a ViewModel approach, you could define the view like this
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical">
<sdk:DataGrid AutoGenerateColumns="False" Height="151" HorizontalAlignment="Left" Margin="52,67,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="190"
ItemsSource="{Binding Items}">
<sdk:DataGrid.Columns>
<sdk:DataGridCheckBoxColumn Binding="{Binding IsSelected}"/>
<sdk:DataGridTextColumn Binding="{Binding Name}"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<Button Content="What is selected?" Width="300" Click="Button_Click"/>
</StackPanel>
</Grid>
Then, you setup the view to be bound against a ViewModel, which contains a property of your PagedCollectionView:
public class ViewModel
{
private PagedCollectionView _items = new PagedCollectionView(
new[]
{new MyItem{Name="Item 1"},
new MyItem{Name="Item 2"},
new MyItem{Name="Item 3"},
new MyItem{Name="Item 4"} });
public PagedCollectionView Items
{
get { return _items; }
}
public string GetSelectedItems()
{
return "Selected items: " +
string.Join(",",
Items.Cast<MyItem>().Where(x => x.IsSelected).
Select(x => x.Name));
}
}
Now, since I don't know your solution that well, I put a piece of ugly code in the code-behind just to show how that the selections are tracked:
private void Button_Click(object sender, RoutedEventArgs e)
{
var viewModel = DataContext as ViewModel;
MessageBox.Show(viewModel.GetSelectedItems());
}
Connecting the View and ViewModel together is done by creating the view, the view model and then stitch them together with the DataContext property of the view.
Like this:
var view = new MyWindow();
var viewModel = new ViewModel();
view.DataContext = viewModel;
That should make the example work.

Silverlight Data Grid twoway Data binding in code

I have a DataGrid with two way binding and not sure why this does not work, any help would be appreciated.
I wanted to dynamically bind to the DataGrid using a twoway binding object.
I used the columns in XAML. If I just set the 'ItemSource" property directly - it works but then the two binding doesn't work - if I change my source in code the Grid doesn't reflect that change.
I created a simple sample to illustrate my setup
Here is the XAML
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="356" d:DesignWidth="590" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid AutoGenerateColumns="False" Height="136" HorizontalAlignment="Left" Margin="71,116,0,0" Name="MyGrid" VerticalAlignment="Top" Width="453" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding Path=Label, Mode=TwoWay}" CanUserReorder="True" CanUserResize="True" CanUserSort="True" Width="Auto" Header="Selected" />
<sdk:DataGridTextColumn Binding="{Binding Path=YValue, Mode=TwoWay}" CanUserReorder="True" CanUserResize="True" CanUserSort="True" Header="Name" Width="*" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</UserControl>
Here is the code behind
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
ObservableCollection<Value> values = new ObservableCollection<Value>();
values.Add(new Value() { Label = "Sony", YValue = 50 });
values.Add(new Value() { Label = "Dell", YValue = 35 });
values.Add(new Value() { Label = "HP", YValue = 27 });
values.Add(new Value() { Label = "HCL", YValue = 17 });
values.Add(new Value() { Label = "Toshiba", YValue = 16 });
PagedCollectionView p = new PagedCollectionView(values);
Binding b = new Binding("ValuesBinding");
b.Mode = BindingMode.TwoWay;
b.Source = values;
MyGrid.SetBinding(DataGrid.ItemsSourceProperty, b);
}
}
public class Value : INotifyPropertyChanged
{
public String Label
{
get
{ return _label; }
set
{
_label = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Label"));
}
}
public Double YValue
{
get
{return _yValue;}
set
{
_yValue = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("YValue"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Double _yValue;
String _label;
}
}
There are a couple of problems that I can see here.
The first line that you use to create your binding is
Binding b = new Binding("ValuesBinding");
This won't do what you want. The string ValuesBinding is being used as a property-path, and the ObservableCollection you're binding the DataGrid to has no property on it named ValuesBinding. Indeed, if you look in the VS Output window, you should see a message such as
System.Windows.Data Error: BindingExpression path error: 'ValuesBinding' property not found on 'System.Collections.ObjectModel.ObservableCollection`1 ...
However, if you remove "ValuesBinding" from the above to leave you with
Binding b = new Binding();
then you get an error about two-way bindings needing a Path. However, you don't need a two-way binding here. You can simply remove the line b.Mode = BindingMode.TwoWay; and the error goes away.
Two-way bindings are used to allow the view-layer to set properties in the view-model layer. The Path specifies where to find the view-model property to set. However, since you're binding straight to a collection, there's no property involved and hence nothing that the view-layer could set.
In your case, this binding doesn't need to be two-way. Changes to the collection itself (e.g. adding or removing items) can still be made, even when using a one-way binding for the ItemsSource. The two-way bindings you have on the Label and YValue properties of your Value class will also work as you expect. Setting a one-way binding on the DataGrid's ItemsSource doesn't make the whole grid read-only.
Finally, I'm not sure why you're creating a binding in code-behind to bind to a collection already available in the code-behind. You can achieve the same by just writing
MyGrid.ItemsSource = values;

Resources