I've created a custom usercontrol that's composed of a AutoCompleteBox with a Selected Item... till now I've implemented it in a way I don't like... I mean I've a XAML view, a Viewmodel and in the viewmodel I load data from a stored procedure.
Since the AutoComplete box is a third party UserControl I've added it to the XAML view and not defined as a custom usercontrol. What's the best practice to do so?
I think the fact that I'm using Catel as MVVM Framework is irrilevant right now..
Thanks
UPDATE #1
My usercontrols need to have some properties that are passed via XAML for example (LoadDefaultValue)
<views:PortfolioChooserView x:Name="PortfolioChooserView" DataContext="{Binding Model.PortfolioModel}" Height="25" LoadDefaultValue="True" Width="150" />
To achieve such a scenario I had to define a dependency property in my PortfolioChooserView defined as
public bool LoadDefaultValue
{
get { return (bool)GetValue(LoadDefaultValueProperty); }
set { SetValue(LoadDefaultValueProperty, value); }
}
public static readonly DependencyProperty LoadDefaultValueProperty = DependencyProperty.Register(
"LoadDefaultValue", typeof(bool), typeof(PortfolioChooserView), new PropertyMetadata(default(bool)));
Since if I would have defined it in Viewmodel only I wouldn't have been able to set it.
The odd thing is that in order to pass it to the viewmodel I had to do such a trick
public PortfolioChooserView()
{
InitializeComponent();
if (!isFirstLoad) return;
Focusable = true;
PortfolioCompleteBox.AllowDrop = true;
PortfolioCompleteBox.Focus();
DragDropManager.AddPreviewDragOverHandler(PortfolioCompleteBox, OnElementDragOver);
DragDropManager.AddDropHandler(PortfolioCompleteBox, OnElementDrop);
DataContextChanged += PortfolioChooserView_DataContextChanged;
isFirstLoad = false;
}
void PortfolioChooserView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dataContext = DataContext as PortfolioModel;
if (dataContext != null)
{
dataContext.LoadDefaultValue = LoadDefaultValue;
dataContext.AllowNull = AllowNull;
//var converter = new PortfolioConverter();
//var portfolio = (Portfolio) converter.Convert(SelectedItem, null, null, CultureInfo.CurrentCulture);
//dataContext.SelectedItem = portfolio;
}
}
But I really dislike to use the DataContextChanged event ...do you see a better approach?
Thank
UPDATE#2
I keep this toghether since It's a related question...
On some viewmodel I used DeferValidationUntilFirstSaveCall = true; in the Constructor to disable the validation at load but my custom usercontrols shows the red border around... what should I do to propagate that info to the nested usercontrols?
Thanks again
See Orc.Controls for tons of examples. It's an open-source library that has a lot of user controls built with Catel, even one with an auto complete box.
As opposed to this, does anybody has figured out a way to show all hidden elements while working in Visual Studio designer(or Blend)?
It's anti-productive to constantly change the default visibility property of elements to be able to see them while editing Xaml files.
After researching, I found this. So here's a tested solution that can be implemented in the view model :
//Declare default Visibility values
private Visibility _processBarVisibility = Visibility.Hidden;
private Visibility _buttonVisibility = Visibility.Hidden;
public ViewModel()
{
//In constructor, override Visibility values if in design mode
DependencyObject dep = new DependencyObject();
if (DesignerProperties.GetIsInDesignMode(dep))
{
_processBarVisibility = Visibility.Visible;
_buttonVisibility = Visibility.Visible;
}
}
If I create a custom control like this:
public class MyControl : ContentControl
{
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(ObservableCollection<object>),
typeof(MyControl),
new PropertyMetadata(null));
public MyControl()
{
// Setup a default value to empty collection
// so users of MyControl can call MyControl.Items.Add()
Items = new ObservableCollection<object>();
}
public ObservableCollection<object> Items
{
get { return (ObservableCollection<object>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
}
And then allow the user to bind to it in Xaml like this:
<DataTemplate>
<MyControl Items="{Binding ItemsOnViewModel}"/>
</DataTemplate>
Then the binding never works! This is due to the Dependency Property Precedence, which puts CLR Set values above Template bindings!
So, I understand why this isn't working, but I wonder if there is a solution. Is it possible to provide a default value of ItemsProperty to new ObservableCollection for lazy consumers of MyControl that just want to add Items programmatically, while allowing MVVM power-users of My Control to bind to the same property via a DataTemplate?
This is for Silverlight & WPF. DynamicResource setter in a style seemed like a solution but that won't work for Silverlight :(
Update:
I can confirm SetCurrentValue(ItemsProperty, new ObservableCollection<object>()); does exactly what I want - in WPF. It writes the default value, but it can be overridden by template-bindings. Can anyone suggest a Silverlight equivalent? Easier said than done! :s
Another Update:
Apparently you can simulate SetCurrentValue in .NET3.5 using value coercion, and you can simulate value coercion in Silverlight using these techniques. Perhaps there is a (long-winded) workaround here.
SetCurrentValue workaround for .NET3.5 using Value Coercion
Value Coercion workaround for Silverlight
Can't you just specify the default property of the dependency property:
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<object>),
typeof(CaseDetailControl),
new PropertyMetadata(new ObservableCollection<object>()));
or am I missing what you are after?
Edit:
ah... in that case how about checking for null on the getter?:
public ObservableCollection<object> Items
{
get
{
if ((ObservableCollection<object>)GetValue(ItemsProperty) == null)
{
this.SetValue(ItemsProperty, new ObservableCollection<object>());
}
return (ObservableCollection<object>)GetValue(ItemsProperty);
}
set
{
this.SetValue(ItemsProperty, value);
}
}
When ObservableCollection properties misbehave, I try throwing out assignments to that property. I find that the references don't translate right and bindings get lost, somehow. As a result, I avoid actually setting ObservableCollection properties (preferring, instead, to clear the existing property and add elements to it). This becomes really sloppy with a DependencyProperty because you're going to call your getter multiple times in your setter. You might want to consider using INotifyPropertyChanged instead. Anyway, here's what it'd look like:
EDIT: Blatantly stole the getter from SteveL's answer. I reworked it a touch so that you only have a single call to GetValue, is all. Good work around.
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> coll = (ObservableCollection<object>)GetValue(ItemsProperty);
if (coll == null)
{
coll = new ObservableCollection<object>();
this.SetValue(ItemsProperty, coll);
}
return coll;
}
set
{
ObservableCollection<object> coll = Items;
coll.Clear();
foreach(var item in value)
coll.Add(item);
}
}
Note that this is depending on your default to set correctly. That means changing the static ItemsProperty default to be a new ObservableCollection of the correct type (i.e. new PropertyMetadata(new ObservableCollection()). You'll also have to remove that setter in the constructor. And note, I've no idea if that'll actually work. If not, you'll want to move to using INotifyPropertyChanged for sure...
I have an issue similar to the following post:
Silverlight DataGridTextColumn Binding Visibility
I need to have a Column within a Silverlight DataGrid be visibile/collapsed based on a value within a ViewModel. To accomplish this I am attempting to Bind the Visibility property to a ViewModel. However I soon discovered that the Visibility property is not a DependencyProperty, therefore it cannot be bound.
To solve this, I attempted to subclass my own DataGridTextColumn. With this new class, I have created a DependencyProperty, which ultimately pushes the changes to the DataGridTextColumn.Visibility property. This works well, if I don't databind. The moment I databind to my new property, it fails, with a AG_E_PARSER_BAD_PROPERTY_VALUE exception.
public class MyDataGridTextColumn : DataGridTextColumn
{
#region public Visibility MyVisibility
public static readonly DependencyProperty MyVisibilityProperty =
DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(MyDataGridTextColumn), new PropertyMetadata(Visibility.Visible, OnMyVisibilityPropertyChanged));
private static void OnMyVisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var #this = d as MyDataGridTextColumn;
if (#this != null)
{
#this.OnMyVisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
}
}
private void OnMyVisibilityChanged(Visibility oldValue, Visibility newValue)
{
Visibility = newValue;
}
public Visibility MyVisibility
{
get { return (Visibility)GetValue(MyVisibilityProperty); }
set { SetValue(MyVisibilityProperty, value); }
}
#endregion public Visibility MyVisibility
}
Here is a small snippet of the XAML.
<DataGrid ....>
<DataGrid.Columns>
<MyDataGridTextColumn Header="User Name"
Foreground="#FFFFFFFF"
Binding="{Binding User.UserName}"
MinWidth="150"
CanUserSort="True"
CanUserResize="False"
CanUserReorder="True"
MyVisibility="{Binding Converter={StaticResource BoolToVisibilityConverter}, Path=ShouldShowUser}"/>
<DataGridTextColumn .../>
</DataGrid.Columns>
</DataGrid>
A couple important facts.
The Converter is indeed defined above in the local resources.
The Converter is correct, it is used many other places in the solution.
If I replace the {Binding} syntax for the MyVisibility property with "Collapsed" the Column does in fact disappear.
If I create a new DependencyProperty (i.e. string Foo), and bind to it I receive the AG_E_PARSER_BAD_PROPERTY_VALUE exception too.
Does anybody have any ideas as to why this isn't working?
Here's the solution I've come up with using a little hack.
First, you need to inherit from DataGrid.
public class DataGridEx : DataGrid
{
public IEnumerable<string> HiddenColumns
{
get { return (IEnumerable<string>)GetValue(HiddenColumnsProperty); }
set { SetValue(HiddenColumnsProperty, value); }
}
public static readonly DependencyProperty HiddenColumnsProperty =
DependencyProperty.Register ("HiddenColumns",
typeof (IEnumerable<string>),
typeof (DataGridEx),
new PropertyMetadata (HiddenColumnsChanged));
private static void HiddenColumnsChanged(object sender,
DependencyPropertyChangedEventArgs args)
{
var dg = sender as DataGrid;
if (dg==null || args.NewValue == args.OldValue)
return;
var hiddenColumns = (IEnumerable<string>)args.NewValue;
foreach (var column in dg.Columns)
{
if (hiddenColumns.Contains ((string)column.GetValue (NameProperty)))
column.Visibility = Visibility.Collapsed;
else
column.Visibility = Visibility.Visible;
}
}
}
The DataGridEx class adds a new DP for hiding columns based on the x:Name of a DataGridColumn and its descendants.
To use in your XAML:
<my:DataGridEx x:Name="uiData"
DataContext="{Binding SomeDataContextFromTheVM}"
ItemsSource="{Binding Whatever}"
HiddenColumns="{Binding HiddenColumns}">
<sdk:DataGridTextColumn x:Name="uiDataCountOfItems">
Header="Count"
Binding={Binding CountOfItems}"
</sdk:DataGridTextColumn>
</my:DataGridEx>
You need to add these to your ViewModel or whatever data context you use.
private IEnumerable<string> _hiddenColumns;
public IEnumerable<string> HiddenColumns
{
get { return _hiddenColumns; }
private set
{
if (value == _hiddenColumns)
return;
_hiddenColumns = value;
PropertyChanged (this, new PropertyChangedEventArgs("HiddenColumns"));
}
}
public void SomeWhereInYourCode ()
{
HiddenColumns = new List<string> {"uiDataCountOfItems"};
}
To unhide, you only need to remove the corresponding name from the list or recreate it without the unhidden name.
I have another solution to this problem that uses an approach similar to the "Binding" property that you find on DataGridTextColumn. Since the column classes are DependencyObjects, you can't directly databind to them, BUT if you add a reference to a FrameworkElement that implements INotifyPropertyChanged you can pass a databinding through to the element, and then use a dependency property to notify the Column that the databinding has changed.
One thing to note is that having the binding on the Column itself instead of the Grid will probably mean that you will want to use a DataContextProxy to get access to the field that you want to bind the Visibility to (the column binding will default to the scope of the ItemSource).
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace XYZ.Controls
{
public class ExtendedDataGridTextColumn : DataGridTextColumn
{
private readonly Notifier _e;
private Binding _visibilityBinding;
public Binding VisibilityBinding
{
get { return _visibilityBinding; }
set
{
_visibilityBinding = value;
_e.SetBinding(Notifier.MyVisibilityProperty, _visibilityBinding);
}
}
public ExtendedDataGridTextColumn()
{
_e = new Notifier();
_e.PropertyChanged += ToggleVisibility;
}
private void ToggleVisibility(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Visibility")
this.Visibility = _e.MyVisibility;
}
//Notifier class is just used to pass the property changed event back to the column container Dependency Object, leaving it as a private inner class for now
private class Notifier : FrameworkElement, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Visibility MyVisibility
{
get { return (Visibility)GetValue(MyVisibilityProperty); }
private set { SetValue(MyVisibilityProperty, value); }
}
public static readonly DependencyProperty MyVisibilityProperty = DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(Notifier), new PropertyMetadata(MyVisibilityChanged));
private static void MyVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var n = d as Notifier;
if (n != null)
{
n.MyVisibility = (Visibility) e.NewValue;
n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
}
}
}
}
}
The datagrid column inherits from DependencyObject instead of FrameworkElement. In WPF this would be no big deal... but in silverlight you can only bind to FrameworkElement objects. So you get the descriptive error message of AG_E_PARSER_BAD_PROPERTY_VALUE when you try.
I don't know how much this will help, but I've run into the lack of dependency property problem with data grid columns myself in my latest project. What I did to get around it, was to create an event in the grid column view model, then when the grid is being assembled in the client, use a closure to subscribe the grid column to the column view model. My particular problem was around width. It starts with the view model class for the grid column, which looks something like this pseudo-code:
public delegate void ColumnResizedEvent(double width);
public class GridColumnViewModel : ViewModelBase
{
public event ColumnResizedEvent ColumnResized;
public void Resize(double newContainerWidth)
{
// some crazy custom sizing calculations -- don't ask...
ResizeColumn(newWidth);
}
public void ResizeColumn(double width)
{
var handler = ColumnResized;
if (handler != null)
handler(width);
}
}
Then there's the code that assembles the grid:
public class CustomGrid
{
public CustomGrid(GridViewModel viewModel)
{
// some stuff that parses control metadata out of the view model.
// viewModel.Columns is a collection of GridColumnViewModels from above.
foreach(var column in viewModel.Columns)
{
var gridCol = new DataGridTextColumn( ... );
column.ColumnResized += delegate(double width) { gridCol.Width = new DataGridLength(width); };
}
}
}
When the datagrid is resized in the application, the resize event is picked up and calls the resize method on the viewmodel the grid is bound to. This in turn calls the resize method of each grid column view model. The grid column view model then raises the ColumnResized event, which the data grid text column is subscribed to, and it's width is updated.
I realise this isn't directly solving your problem, but it was a way I could "bind" a view model to a data grid column when there are no dependency properties on it. The closure is a simple construct that nicely encapsulates the behaviour I wanted, and is quite understandable to someone coming along behind me. I think it's not too hard to imagine how it could be modified to cope with visibility changing. You could even wire the event handler up in the load event of the page/user control.
Chris Mancini,
you do not create binding to "Binding" property of data grid column. Well, you write "{Binding User.UserName}", but it doesn't create binding, because (as zachary said) datagrid column doesn't inherit from FrameworkElement and hasn't SetBinding method.
So expression "{Binding User.UserName}" simply creates Binding object and assign it to Binding property of column (this property is type of Binding).
Then datagrid column while generates cells content (GenerateElement - protected method) uses this Binding object to set binding on generated elements (e.g. on Text property of generated TextBlock) which are FrameworkElements
GreatTall1's solution is great, but it need to bit change to make it work.
var n = d as Notifier;
if (n != null)
{
//Assign value in the callback will break the binding.
//n.MyVisibility = (Visibility)e.NewValue;
n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
}
Note that the problem isn't just as simple as 'Visibility' not being a dependency property. In a DataGrid the columns aren't part of the visual 'tree' so you can't use AncestorType even in WPF (or Silverlight 5).
Here's a couple WPF related links (please comment if any of these work for Silverlight - sorry I don't have time to test now)
Has a really nice explanation of the problem and failures of certain solutions (and a clever solution):
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
And a couple StackOverflow questions:
WPF Hide DataGridColumn via a binding
Binding Visible property of a DataGridColumn in WPF DataGrid
This works on a data grid template column:
public class ExtendedDataGridColumn : DataGridTemplateColumn
{
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register("Visibility", typeof(Visibility), typeof(DataGridTemplateColumn), new PropertyMetadata(Visibility.Visible, VisibilityChanged));
public new Visibility Visibility
{
get { return (Visibility)GetValue(VisibilityProperty); }
set { SetValue(VisibilityProperty, value); }
}
private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((DataGridTemplateColumn)d != null)
{
((DataGridTemplateColumn)d).Visibility = (Visibility)e.NewValue;
}
}
}
From your MyDataGridTextColumn class, you could get the surrounding DataGrid.
Then you get your ViewModel out of the DataContext of the DataGrid and add a handler to the PropertyChanged event of your ViewModel. In the handler you just check for the property name and its value and change the Visibility of the Column accordingly.
Its not quite the best solution, but it should work ;)
I find that when I make Dependency Properties, most of them conflict with names of properties in the UserControl, e.g Background, Width, etc. so my strategy is to prefix all of my custom properties with "The" so I have, e.g.
TheBackground
TheWidth
etc.
I tried using the "new" keyword which gets rid of the warning, but that leads to conflicts at runtime.
Has anyone come upon better naming strategies for DependencyProperties in custom user controls?
public partial class SmartForm : UserControl
{
public SmartForm()
{
InitializeComponent();
DataContext = this;
TheBackground = "#FFD700";
}
#region DependencyProperty: TheBackground
public string TheBackground
{
get
{
return (string)GetValue(TheBackgroundProperty);
}
set
{
SetValue(TheBackgroundProperty, value);
}
}
public static readonly DependencyProperty TheBackgroundProperty =
DependencyProperty.Register("TheBackground", typeof(string), typeof(SmartForm),
new FrameworkPropertyMetadata());
#endregion
}
If your UserControl has a background property, why do you have to add another one?
What is this new background a background for? "The"? No? Then what background does it control?
Finish this sentence: "This is the background color for the XXXX control."
The property name should now be XXXXBackground.
You mean you want a override??
In my case I don't put Width as a DepedencyProperty
in my custom control I have:
public double Width
{
get
{
if (_backgroundImage != null)
return _backgroundImage.Width;
else
return double.NaN;
}
set
{
if (_backgroundImage != null)
_backgroundImage.Width = value;
}
}
The compiler shows me a warning but everything seems to work.