Custom Usercontrol with MVVM and Catel - wpf

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.

Related

How can I bind Pushpin location in WPF using MVVM?

I'm developing a Desktop application that uses Bing maps and MVVM.
In the application, a user adds a Pushpin in the map by double clicking on it, the Pushpin location gets saved in an Event class and the Event class is sent through a WCF Service.
I would like to get the Latitude and Longitude from the Pushpin using data binding, however the compiler complains about DependencyProperty when I try to do that.
I managed to set the Latitude and Longitude in the ViewModel from the View, however I don't know if it's valid in MVVM. I have seen examples using MapsItemControls but I don't understand them.
ViewModel
private Event evt;
public Event Evt
{
get
{
return this.evt;
}
set
{
this.evt = value;
OnPropertyChanged("Event");
}
}
Map xaml
<m:Map Grid.RowSpan="5" Grid.Column="3" Margin="3"
Name="operatorMap"
CredentialsProvider="Map_key"
Center="19.4000,-99.1333"
ZoomLevel="5"
MouseDoubleClick="SetPushpinLocation" />
CodeBehind
private MaintenanceFormViewModel viewModel = new MaintenanceFormViewModel();
private Pushpin pin = null;
public MainWindow()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.DataContext = this.viewModel;
};
}
private void SetPushpinLocation(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
Point mousePosition = e.GetPosition((UIElement)sender);
Location pinLocation = operatorMap.ViewportPointToLocation(mousePosition);
if (pin == null)
{
pin = new Pushpin();
operatorMap.Children.Add(pin);
}
pin.Location = pinLocation;
this.viewModel.Evt.Latitude = pinLocation.Latitude;
this.viewModel.Evt.Longitude = pinLocation.Longitude;
}
Bing Maps uses the attached property MapLayer.Position for positioning elements on the map.
Given a view model with a property of type Location
public class ViewModel : INotifyPropertyChanged
{
private Location location;
public Location Location
{
get { return location; }
set
{
location = value;
OnPropertyChanged("Location");
}
}
...
}
you can bind the position of a Pushpin like this:
<bm:Pushpin bm:MapLayer.Position="{Binding Location}"/>
Note that in the Windows Store App version of the Bing Maps SDK there seems to be a bug when you try to setup a binding in XAML. It says (still with Bing Maps SDK version 1.313.825.0)
Failed to assign to property 'Bing.Maps.MapLayer.Position'
You can however create a binding in code behind:
pushpin.SetBinding(MapLayer.PositionProperty,
new Binding { Path = new PropertyPath("Location") });
Personally, I'd add an attached property to the Bing map which would allow you to bind the lat/long to properties to your ViewModel. This would follow the MVVM pattern.
Google "attached property wpf" for a tutorial on attached properties, there are some good ones out there.
This is not to say that using code behind is bad: usually I get it working with code behind first, then port it into an attached property to adhere to the MVVM pattern, and for reusability and maintainability.
You mentioned an error related to dependecy properties. These are completely different to attached properties.
You add a dependency property to a user control that you write yourself.
You add an attached property to another 3rd party control you cannot alter or do not have the source code for. The rule of thumb is this: if you start with any code behind in a user control, you can shift it into an attached property to keep in line with the MVVM pattern.
Yes, attached properties are a bit of a learning curve, but persevere: this is one technique you will have to master before you can become an MVVM expert.

Getting Value from ViewModel through DataContext WITHOUT Binding?

New to WPF. I am creating UserControls that need read access to the ViewModel state to do their thing. I currently use the following technique:
public partial class ControlBar : UserControl
{
private static readonly DependencyProperty URLProperty =
DependencyProperty.Register("URL", typeof(string), typeof(ControlBar),
new UIPropertyMetadata(null));
public ControlBar()
{
InitializeComponent();
SetBinding(URLProperty, "CurrentPage.URL");
Pin.Click += Pin_Click;
}
private void Pin_Click(object sender, RoutedEventArgs e)
{
var URL = (string)GetValue(URLProperty);
}
}
Is this the correct way and is it not overkill to set up a long-term binding for each variable I need access to? Or can you do something like:
GetValue(new Path("CurrentPage.URL.....
I made up the above obviously.
Thanks!
In general data-binding is the way to go. However sometimes when you are creating controls that have view-specific concerns for which data-binding will not be appropriate.
In those cases you will want to be able to interact with the DependencyProperty to set it and know when it changes. I have been following a pattern that I picked up from a Charles Petzold article in MSDN magazine.
My answer to another question shows the pattern for creating a DependencyProperty for a UserControl Stack Overflow: Dependency Property In WPF/SilverLight
Again, data-binding to a view model will likely solve your problem, but a DependencyProperty may come in useful depending on the situation.
Update in response to comment:
In many situations you can data bind your in a UserControl without using a DependencyProperty. For example if you have a TextBlock that displays a name you would put a TextBlock in the XAML of the UserControl
<TextBlock Text="{Binding Path=NameString}" />
In the view model which is present in the DataContext you would have a property NameString and if the TextBlock is to update the display when the NameString property changes the view model should implement INotifyPropertyChanged and the property should fire the PropertyChanged event with the name of the property sent along with the event.
protected string _NameString;
public string NameString
{
get { return _NameString; }
set { _NameString = value: Notify("NameString"); }
}
Where Notify is a method that checks the PropertyChanged event for null and sends the event if not null.
This works well if everywhere that you want to use the UserControl has a view model with a Name property. The great thing is that the UserControl can pick up on the DataContext of wherever it is hosted and bind to an external view model.
When you want to start binding the same UserControl to different properties is one place that you may want to use a DependencyProperty. In that case you could make a UserControl with a DependencyProperty and bind it to different properties
<my:SampleControl NameString="{Binding Path=GivenName}" />
<my:SampleControl NameString="{Binding Path=FamilyName}" />
And then have an internal view model that the DependencyProperty change handler updates when the bound property changes.
Update: No DependencyProperty or binding
You can always add an ordinary C# property to the UserControl and pass the data in that way.
public MyClass Data { get; set; }
Then in the code-behind of the UserControl you can simply use the property:
if (this.Data != null)
{
this.textBox1.Text = Data.NameString;
}
Update in response to comment:
Another way to access the view model in code is to cast the DataContext to your view model type:
MyClass data = this.DataContext as MyClass;
if (data != null)
{
// do something
this.textBox1.Text = data.NameString;
}

Using WPF, MVVM and Bindings with a WinForm UserControl, how to integrate successfully?

I have a WinForm UserControl inside a WPF window and the WPF code is using the MVVM pattern.
What is the best way to successfully integrate the WinForm control into the MVVM pattern?
Can I use some form of binding from the WPF side?
Let's say that I want to handle some events from the WF control, is there a way to fully go MVVM?
Thanks.
Note that this doesn't really answer the questions (I should have read better). If you're interested in using a WPF control in a WinForms app, here's an approach. My scenario is:
1) Have a WinForms control that is used many places in my app.
2) Want to develop a WPF implementation that will use the MVVM pattern.
3) Want to write the control as a proper WPF control complete with dependency properties so it can be used properly when my app is eventually all WPF.
4) Want to keep the same WinForms control and API to not break existing client code in my app.
Most everything was straightforward except for having my WinForms control raise events when properties of my WPF control changed. I wanted to use a binding but since the source of a binding must be a DependencyObject and a System.Windows.Forms.UserControl is not, I had to make a simple nested class. I wrote my WPF control exactly as if I was integrating it into a WPF application, and just did some extra thunking to get my WinForms wrapper to work.
Here's code for my WPF control:
public partial class MonkeySelector : UserControl
{
public static readonly DependencyProperty SelectedMonkeyProperty =
DependencyProperty.Register(
"SelectedMonkey", typeof(IMonkey),
typeof(MonkeySelector));
public MonkeySelector()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
// Note: No code is shown for binding the SelectedMonkey dependency property
// with the ViewModel's SelectedMonkey property. This is done by creating
// a Binding object with a source of ViewModel (Path = SelectedMonkey) and
// target of the SelectedMonkey dependency property. In my case, my
// ViewModel was a resource declared in XAML and accessed using the
// FindResource method.
}
public IMonkey SelectedMonkey
{
get { return (IMonkey)GetValue(SelectedMonkeyProperty); }
set { SetValue(SelectedMonkeyProperty, value); }
}
}
Here's the code for my WinForms control:
public partial class WinFormsMonkeySelector : UserControl
{
public event EventHandler SelectedMonkeyChanged;
private MonkeySelector _monkeySelector;
private WpfThunker _thunker;
public WinFormsMonkeySelector()
{
InitializeComponent();
_monkeySelector = new MonkeySelector();
_elementHost.Child = _monkeySelector;
System.Windows.Data.Binding binding = new System.Windows.Data.Binding("SelectedMonkey");
binding.Source = _monkeySelector;
binding.Mode = System.Windows.Data.BindingMode.OneWay;
_thunker = new WpfThunker(this);
// Note: The second parameter here is arbitray since we do not actually
// use it in the thunker. It cannot be null though. We could declare
// a DP in the thunker and bind to that, but that isn't buying us anything.
System.Windows.Data.BindingOperations.SetBinding(
_thunker,
MonkeySelector.SelectedMonkeyProperty,
binding);
}
protected virtual void OnSelectedMonkeyChanged()
{
if (SelectedMonkeyChanged != null)
SelectedMonkeyChanged(this, EventArgs.Empty);
}
public IMonkey SelectedMonkey
{
get { return _monkeySelector.SelectedMonkey; }
set { _monkeySelector.SelectedMonkey = value; }
}
private class WpfThunker : System.Windows.DependencyObject
{
private WinFormsMonkeySelector _parent;
public WpfThunker(WinFormsMonkeySelector parent)
{
_parent = parent;
}
protected override void OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
// Only need to check the property here if we are binding to multiple
// properties.
if (e.Property == MonkeySelector.SelectedMonkeyProperty)
_parent.OnSelectedMonkeyChanged();
}
}
}
Personally, I would handle this by creating a WPF UserControl that wraps the Windows Forms control. This would allow you to encapsulate all of the required code-behind into your WPF Control, and then use it in a pure MVVM manner.
It will be difficult to stay "pure" MVVM using a Windows Forms control directly, as Windows Forms controls typically require a different binding model, as well as typically requiring direct event handling.
You might have a look at the WAF Windows Forms Adapter. It shows a possible way to use Windows Forms together with MVVM.

How do I load user controls dynamically?

How can I load a user control[s] in a window dynamically (using code at runtime)?
I'd highly recommend having a look at Prism, since composite user interfaces is what it's for. However, since this would require you refactoring your entire application, I'll also answer your question directly.
If you want a single user control in a container, put a ContentControl in your XAML and then set the Content property. If you are using a view model, you could bind Content to a FrameworkElement property on the view model:
contentControlInstance.Content = new CustomUserControl();
If you want multiple controls in a list, use an ItemsControl and assign an ObservableCollection<> to the ItemsSource property. If you are using a view model, you could bind ItemsSource to an ObservableCollection property on the View Model.
Then you can just add/remove views from that ObservableCollection:
private ObservableCollection<FrameworkElement> views =
new ObservableCollection<FrameworkElement>();
private void Initialize()
{
itemsControl.ItemsSource = views;
}
private void AddView(FrameworkElement frameworkElement)
{
views.Add(frameworkElement);
}
For adding multiple controls you need container.
Suppose you have a StackPanel container "myStack"
<Window ..>
<StackPanel Name="MyStack" />
</Window>
You can create control dynamically and add it to container. See code below
void AddButtons()
{
Button B1=new Button(),B2=new Button(), B3=new Button();
B1.Content="Hello";
B2.Content="First";
B3.content="Application";
// Now you can set more properties like height, width, margin etc...
MyStack.Children.Add(B1);
MyStack.Children.Add(B2);
MyStack.Children.Add(B2);
}
Or use binding. Here's a really crude example showing how different WPF controls can be shown in a single WPF window using ContentControl and binding (which is what a toolkit like Prism or Caliburn Micro does).
XAML:
<UserControl x:Class="ViewA">
...
<UserControl/>
<UserControl x:Class="ViewB">
...
<UserControl/>
Code:
void ShowViewModelDialog (object viewModel)
{
var host = new MyViewHost();
FrameworkElement control = null;
string viewModelName = viewModel.GetType().Name;
switch (viewModelName )
{
case ("ViewModelA"):
control = new ViewA();
break;
case ("ViewModelB"):
control = new ViewB();
break;
default:
control = new TextBlock {Text = String.Format ("No view for {0}", viewModelName);
break;
}
if (control!=null) control.DataContext = viewModel;
host.DataContext = control;
host.Show(); // Host window will show either ViewA, ViewB, or TextBlock.
}

How to bind DataGridColumn.Visibility?

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 ;)

Resources