I'm using the WPF DataGrid from the WPF Toolkit.
I added a templated column to my DataGrid, which has a CheckBox in each cell. Now how do I access the values within these cells?
My other columns in the DataGrid come from a DataSet. I can access these, but I cannot get to the values of the DataGridTemplateColumn I added to the DataGrid.
Anyone have any ideas?
your into pulling stuff out of the visual tree now. and thats hard work, you cant find the binding because that is buried in the cell template. what i did was add my own column for this kind of stuff, the column derives from DataGridBoundColumn, which means it has a binding like all the others: ( i wrote it a while ago, it could probably do with some looking at ) This lets me just use a straight binding. i dont have to set a cell template, i can just use a DataTemplate which i like better.
public class DataGridReadOnlyObjectDisplayColumn : DataGridBoundColumn {
public DataGridReadOnlyObjectDisplayColumn() {
//set as read only,
this.IsReadOnly = true;
}
/// <summary>
/// Gets and Sets the Cell Template for this column
/// </summary>
public DataTemplate CellTemplate {
get { return (DataTemplate)GetValue(CellTemplateProperty); }
set { SetValue(CellTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for CellTemplate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CellTemplateProperty =
DependencyProperty.Register("CellTemplate", typeof(DataTemplate), typeof(DataGridReadOnlyObjectDisplayColumn), new UIPropertyMetadata(null));
protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {
//create the simple field text block
ContentControl contentControl = new ContentControl();
contentControl.Focusable = false;
//if we have a cell template use it
if (this.CellTemplate != null) {
contentControl.SetValue(ContentControl.ContentTemplateProperty, this.CellTemplate);
}
//set the binding
ApplyBinding(contentControl, ContentPresenter.ContentProperty);
//return the text block
return contentControl;
}
/// <summary>
/// Assigns the Binding to the desired property on the target object.
/// </summary>
internal void ApplyBinding(DependencyObject target, DependencyProperty property) {
BindingBase binding = Binding;
if (binding != null) {
BindingOperations.SetBinding(target, property, binding);
}
else {
BindingOperations.ClearBinding(target, property);
}
}
protected override System.Windows.FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
//item never goes into edit mode it is a read only column
return GenerateElement(cell, dataItem);
}
}
now if you can get to the column you can get to the binding on the column. if you can get to the cell then you can find the data item (the row data). then what i do is follow the binding to get the cell value. it is really inefficient, and it is a hack. but it works. to follow the binding i use this.
private Object GetCellValue(Binding columnBinding, object dataSource) {
Object valueField = null;
if (columnBinding != null) {
BindingEvaluator bindingEvaluator = new BindingEvaluator();
//copy the binding
Binding binding = new Binding();
binding.Path = columnBinding.Path;
binding.Source = dataSource;
//apply the binding
BindingOperations.SetBinding(bindingEvaluator, BindingEvaluator.BindingValueProperty, binding);
//get the current cell item
valueField = bindingEvaluator.BindingValue as IValueField;
}
return valueField;
}
and the last piece is a helper class called BindingEvaluator which has one dp, that i use to follow the binding
public class BindingEvaluator : DependencyObject {
/// <summary>
/// Gets and Sets the binding value
/// </summary>
public Object BindingValue {
get { return (Object)GetValue(BindingValueProperty); }
set { SetValue(BindingValueProperty, value); }
}
// Using a DependencyProperty as the backing store for BindingValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BindingValueProperty =
DependencyProperty.Register("BindingValue", typeof(Object), typeof(BindingEvaluator), new UIPropertyMetadata(null));
}
and i call it like so:
var valueField = this.GetCellValue(column.Binding as Binding, datagrid.CurrentCell.Item);
Related
I have a custom control with a ViewModel. In this control I make programatically binding from the properties of the control to the ViewModel.
When I use the control and I make a binding to the property the value's aren't updated. I have the this for the bindings
In the customControl ViewModel
private string _InitValue;
public string InitValue
{
get { return _InitValue; }
set { _InitValue = value; NotifyPropertyChanged();}
}
In the customControl I set the binding
initValueBinding = new Binding();
initValueBinding.Source = LocalDataContext;
initValueBinding.Path = new PropertyPath("InitValue");
initValueBinding.Mode = BindingMode.OneWayToSource;
initValueBinding.BindsDirectlyToSource = true;
initValueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;
BindingOperations.SetBinding(this, PlusMinControl.InitValueProperty, initValueBinding);
The InitValueProperty is a dependency property.
public static DependencyProperty InitValueProperty = DependencyProperty.Register(nameof(InitValue), typeof(string), typeof(PlusMinControl), new PropertyMetadata( new PropertyChangedCallback(test)) );
private static void test(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
;
}
public string InitValue
{
get { return ((string)(base.GetValue(PlusMinControl.InitValueProperty))); }
set { base.SetValue(PlusMinControl.InitValueProperty, value); }
}
For the implementation of the customControl
<plm:PlusMinControl InitValue="{Binding InitVal}" />
In the code behind I set the datacontext and I've InitVal defined as a normal property.
When I debug the code I can trace the changes till the PropertyChangedCallback but the property in the viewmodel isn't updated.
Can anyone tell me what I do wrong? And how I should fix this.
Thank you!
you have inverted and mixed same init :
this makes the binding property=viewmodel
<plm:PlusMinControl InitValue="{Binding InitVal}" />
and this too
"initValueBinding = new Binding();....."
this must be enough, remove the binding code
<plm:PlusMinControl InitialValue ="{Binding InitValue}" />
change the property to InitialValue in the control
change BindingMode.OneWayToSource to BindingMode.TwoWay
I'm trying to create a WPF ListView that can bind its SelectedItems property.
I inherit from ListView. When the ListView's SelectionChanged event is raised, I read the ListView's SelectedItems property, and set my DependencyProperty's value to it.
I then add a BetterListView object to the XAML and bind its SelectedItemsBindable property to the viewmodel.
And although it looks like everything works - when I break at the view model's set method, I see that although SelectedItemsBindable was set to a collection inside BetterListView, the viewmodel gets this value as null! What am I doing wrong?
class BetterListView : ListView
{
[Bindable(true)]
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IList SelectedItemsBindable
{
get { return GetValue(SelectedItemsBindableProperty) as IList; }
set
{
SetValue(SelectedItemsBindableProperty, value);
}
}
public static readonly DependencyProperty SelectedItemsBindableProperty = DependencyProperty.Register(
"SelectedItemsBindable",
typeof(IList),
typeof (BetterListView),
new FrameworkPropertyMetadata(new List<object>(),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var items = SelectedItems.Cast<object>().ToList();
SelectedItemsBindable = items;
}
#region constructor
public BetterListView()
{
SelectionChanged += OnSelectionChanged;
}
#endregion
}
I have an application where IsEnabled binding is used.
I also need to remove this binding for a short moment.
I have created an ControlBehavior with an attached property to use as a temporary storageplace. ClearIsEnabledBinding will set the binding used in this attached property. RestoreIsEnabeldBinding is supposed to return the binding to the original place. This doesn't work. When the binding is cleared the attached property also is cleared.
The scenario is like this. I have a textbox with a binding for IsEnabled to the viewmodel with a converter. When I use a specific function all IsEnabled should be true regardless of the value in the viewmodel. This is easy just delete the binding and set to true. But when I return from this function I need to restore the binding to it's original binding to the viewmodel with a converter. So I need to save the entire bindingexpression somewhere and then "put" it back
My class is as follow:
Any suggestions to what I'm doing wrong?
public partial class ControlBehavior
{
public static readonly DependencyProperty IsEnabledBindingProperty = DependencyProperty.RegisterAttached(
"IsEnabledBinding",
typeof(BindingExpression),
typeof(ControlBehavior),
new PropertyMetadata(null));
public static void SetIsEnabledBinding(DependencyObject element, BindingExpression value)
{
if (value != null)
{
element.SetValue(IsEnabledBindingProperty, value);
SetIsEnabledBindingSet(element, true);
}
}
public static BindingExpression GetIsEnabledBinding(DependencyObject element)
{
var obj = element.GetValue(IsEnabledBindingProperty);
return (BindingExpression) obj;
}
public static readonly DependencyProperty IsEnabledBindingSetProperty = DependencyProperty.RegisterAttached(
"IsEnabledBindingSet",
typeof(bool),
typeof(ControlBehavior),
new PropertyMetadata(false));
public static void SetIsEnabledBindingSet(DependencyObject element, bool value)
{
element.SetValue(IsEnabledBindingSetProperty, value);
}
public static bool GetIsEnabledBindingSet(DependencyObject element)
{
return (bool)element.GetValue(IsEnabledBindingSetProperty);
}
public static void ClearIsEnabledBinding(DependencyObject element)
{
SetIsEnabledBinding(element, ((Control)element).GetBindingExpression(UIElement.IsEnabledProperty));
((Control)element).SetBinding(UIElement.IsEnabledProperty, new Binding());
}
public static void RestoreIsEnabledBinding(DependencyObject element)
{
if (!GetIsEnabledBindingSet(element))
{
return;
}
((Control)element).SetBinding(UIElement.IsEnabledProperty, GetIsEnabledBinding(element).ParentBindingBase);
}
}
We've used the below class to do what I think you're trying to achieve:
/// <summary>
/// Utilities to be used with common data binding issues
/// </summary>
class DataBindingUtils
{
private static readonly DependencyProperty DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof(Object),
typeof(DependencyObject),
new UIPropertyMetadata(null));
/// <summary>
/// Get a binding expression source value (using the PropertyPath), from MSDN forums:
/// "Yes, there is one (class to provide the value) inside WPF stack, but it's an internal class"
/// Get source value of the expression by creating new expression with the same
/// source and Path, attaching this expression to attached property of type object
/// and finally getting the value of the attached property
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static Object Eval(BindingExpression expression)
{
// The path might be null in case of expression such as: MyProperty={Binding} - in such case the value we'll get
// will be the DataContext
string path = null;
if (expression.ParentBinding != null && expression.ParentBinding.Path != null)
{
path = expression.ParentBinding.Path.Path;
}
Binding binding = new Binding(path);
binding.Source = expression.DataItem;
DependencyObject dummyDO = new DependencyObject();
BindingOperations.SetBinding(dummyDO, DummyProperty, binding);
object bindingSource = dummyDO.GetValue(DummyProperty);
BindingOperations.ClearBinding(dummyDO, DummyProperty);
return bindingSource;
}
}
THe scenario is like this. I have a textbox with a binding for IsEnabled to the viewmodel with a converter. When I use a specific function all IsEnabled should be true regardless of the value in the viewmodel. This is easy just delete the binding and set to true. But when I return from this function I need to restore the binding to it's original binding to the viewmodel with a converter
Instead of dealing with binding you can provide mechanism to override IsEnabled value temporarily. I assume 2 thins: 1) you are binding to something (lets call it IsEnabled) 2) you are making some kind of edit mode, which should override binding value temproarily:
bool _isEditMode;
public bool IsEditMode
{
get { return _isEditMode; }
set
{
_isEditMode = value;
OnPropertyChanged(nameof(IsEnabled)); // update IsEnabled when IsEditMode changed
}
}
bool _isEnabled;
public bool IsEnabled
{
get
{
return IsEditMode || _isEnabled; // return true if IsEditMode == true or actual value otherwise
}
set
{
_isEnabled = value;
OnPropertyChanged();
}
}
Now if you bind
<TextBox IsEnable="{Binding IsEnabled, Converter= ...}" ... />
then as soon as you set IsEditMode = true textbox will become enabled. If you set IsEditMode = false textbox will be again enabled or disabled depending on the value of IsEnabled viewmodel property.
I wrote a custom control derived from Progressbar which implements animations on valuechange (the value fills up with a doubleanimation until the target is reached).
var duration = new Duration(TimeSpan.FromSeconds(2.0));
var doubleanimation = new DoubleAnimation(value, duration)
{
EasingFunction = new BounceEase()
};
BeginAnimation(ValueProperty, doubleanimation);
A new Property "TargetValue" is used, because the ControlTemplate used for the ProgressBar has to show the new value straight after changing it. For this the ProgressEx contains the following:
public static readonly DependencyProperty TargetValueProperty = DependencyProperty.Register("TargetValue", typeof (int), typeof (ProgressEx), new FrameworkPropertyMetadata(0));
public int TargetValue
{
get { return (int)GetValue(TargetValueProperty); }
set
{
if (value > Maximum)
{
//Tinting background-color
_oldBackground = Background;
Background = FindResource("DarkBackgroundHpOver100") as LinearGradientBrush;
}
else
{
if (_oldBackground != null)
Background = _oldBackground;
}
SetValue(TargetValueProperty, value);
Value = value;
}
}
When the TargetValue exceeds the maximum i will use a different color defined in xaml. This works really good - But. Now i want to use this bar in a listview where it is bind to some data. Problem is, the setter is not called in this case, so no animation is executed, even when the value is changed via TargetValue={Binding ProgressValue}. I know that the framework will always call GetValue and SetValue directly and no logic should be supplied, but is there a way around this?
Thanks in advance.
The CLR style getters and setters of DependencyPropertys are not meant to be called by the Framework... they are there for developers to use in code. If you want to know when a DependencyProperty value has changed, you need to add a handler:
public static readonly DependencyProperty TargetValueProperty =
DependencyProperty.Register("TargetValue", typeof (int), typeof (ProgressEx),
new FrameworkPropertyMetadata(0, OnTargetValueChanged));
private static void OnTargetValueChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
// Do something with the e.NewValue and/or e.OldValue values here
}
I've created a custom control that extends the RichTextBox so that I can create a binding for the xaml property. It all works well as long as I just update the property from the viewmodel but when I try to edit in the richtextbox the property is not updated back.
I have the following code in the extended version of the richtextbox.
public static readonly DependencyProperty TextProperty = DependencyProperty.Register ("Text", typeof(string), typeof(BindableRichTextBox), new PropertyMetadata(OnTextPropertyChanged));
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var rtb = d as BindableRichTextBox;
if (rtb == null)
return;
string xaml = null;
if (e.NewValue != null)
{
xaml = e.NewValue as string;
if (xaml == null)
return;
}
rtb.Xaml = xaml ?? string.Empty;
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
In the view I've set the binding like such
<Controls:BindableRichTextBox Text="{Binding XamlText, Mode=TwoWay}"/>
In the viewmodel I've created the XamlText as a normal property with the NotifyPropertyChanged event being called on updates.
I want the bound XamlText to be updated when the user enters texts in the RichTextBox either on lostfocus or directly during edit, it doesn't really matter.
How can I change the code to make this happen?
You will need to listen to changes to the Xaml-property of the BindableRichTextBox and set the Text-property accordingly. There is an answer available here describing how that could be achieved. Using the approach described in that would the result in the following code (untested):
public BindableRichTextBox()
{
this.RegisterForNotification("Xaml", this, (d,e) => ((BindableRichTextBox)d).Text = e.NewValue);
}
public void RegisterForNotification(string propertyName, FrameworkElement element, PropertyChangedCallback callback)
{
var binding = new Binding(propertyName) { Source = element };
var property = DependencyProperty.RegisterAttached(
"ListenAttached" + propertyName,
typeof(object),
typeof(UserControl),
new PropertyMetadata(callback));
element.SetBinding(property, binding);
}