Master/Detail Toolbar Command binding - wpf

I have following App with a toolbar, Masters list and detail view:
The detail is "injected" via ContentControl.
The detail contains a UserControl, which contains a ScrollViewer and so on. At some point there is a "ZoomPanControl" (not mine) which provides a command "FitView".
I want to execute the command "FitView" from my toolbar for the currently active detail view.
My toolbar button looks like this:
<fluent:Button x:Name="FitButton" Command="{Binding ?WhatGoesHere?}" Header="Fit View"/>
I can not figure out how to bind the command property of the toolbar button to the currently active ZoomPanControl. I don't even "see" this control when doing the command binding.
Any hint how this problem is generally solved is highly appreciated.

public static class WpfHelper
{
public static IEnumerable<DependencyObject> GetVisualChildsRecursive(this DependencyObject parent)
{
if (parent == null)
throw new ArgumentNullException("parent");
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
var v = VisualTreeHelper.GetChild(parent, i);
yield return v;
foreach (var c in GetVisualChildsRecursive(v))
yield return c;
}
}
}
//Command Execute
this.DetailView.GetVisualChildsRecursive().OfType<ZoomPanControl>().First().FitView();
//Command CanExecute
this.DetailView.GetVisualChildsRecursive().OfType<ZoomPanControl>().Any();

Here's how I solved the problem. Luckily I have access to the source code of the ZoomPanControl.
First I have implemented a DependencyProperty in the ZoomPanControl for the "FitView" command like this:
public static readonly DependencyProperty FitCommandDepPropProperty = DependencyProperty.Register(
"FitCommandDepProp", typeof (ICommand), typeof (ZoomAndPanControl), new PropertyMetadata(default(ICommand)));
public ICommand FitCommandDepProp
{
get { return (ICommand) GetValue(FitCommandDepPropProperty); }
set { SetValue(FitCommandDepPropProperty, value); }
}
In the "OnApplyTemplate()" method of the control I set the dependency property:
FitCommandDepProp = FitCommand;
In the detail-View of my application I bind the command-dependency-property to my ViewModel like this:
<zoomAndPan:ZoomAndPanControl x:Name="zoomAndPanControl"
FitCommandDepProp="{Binding FitCommand, Mode=OneWayToSource}"
The important part is Mode=OneWayToSource. This "forwards" the command from the ZoomPanControl to my detail-viewmodel.
Detail-viewmodel has property of ICommand to bind to. From this point on I have the command in my viewmodel logic. I have implemented a mechanism to pass the FitCommand to the viewmodel which is bound to the toolbar. You can use a event or whatever you prefer to pass the command around.
The viewmodel of the toolbar has again a ICommand property for the FitCommand.
public ICommand FitCommand
{
get { return _fitCommand; }
set
{
if (Equals(value, _fitCommand)) return;
_fitCommand = value;
NotifyOfPropertyChange(() => FitCommand);
}
}
In the toolbar-view I bind simply to this property:
<fluent:Button x:Name="FitButton" Command="{Binding FitCommand}" Header="Fit View"/>
After that, the view commands are available for each detail-view separately.
But I have no idea how to solve this without access to the source code of the ZoomPanControl.

Related

TabControl region, How to pass parameter to child region? WPF - Prism

I'm using prism regions in order to create dynamic TabControl. But I'm having a problem passing the object from TabItem (parent view) to its child regions.
The below is the code I'm using to build the TabControl.
Shell:
xaml
<ContentControl regions:RegionManager.RegionName="ShellProjectRegion" />
ShellViewModel
regionManager.RegisterViewWithRegion(ShellProjectRegion, typeof(ProjectTabView));
ProjectTabView:
xaml
<TabControl regions:RegionManager.RegionName="ProjectTabRegion">
ProjectTabViewModel
container.RegisterType<object, ProjectView>(typeof(ProjectView).FullName);
ProjectView:
xaml
<Grid>
<ContentControl regions:RegionManager.RegionName="ProjectExplorerRegion"
regions:RegionManager.RegionContext="{Binding}" />
</Grid>
ProjectViewModel
public class ProjectViewModel : BindableBase, INavigationAware, IActiveAware {
private ProjectItem _project;
public ProjectItem Project {
get { return _project; }
set { SetProperty(ref _project, value); }
}
public ProjectViewModel(IRegionManager regionManager) {
regionManager.RegisterViewWithRegion("ProjectExplorerRegion", typeof(ProjectExplorerView));
}
public void OnNavigatedTo(NavigationContext navigationContext) {
Project = (ProjectItem)navigationContext.Parameters["project"];
}
}
ProjectExplorerView:
xaml.cs
public ProjectExplorerView(IUnityContainer container) {
InitializeComponent();
var vm = container.Resolve<ProjectExplorerViewModel>();
RegionContext.GetObservableContext(this).PropertyChanged += (s, e) => {
var context = (ObservableObject<object>)s;
var projectVm = (ProjectViewModel)context.Value;
vm.ParentProjectInfo = projectVm.Project.ProjectInfo;
};
DataContext = vm;
}
Note: Please note that in the last piece of code inside the ProjectExplorerView.xaml.cs the view constructor gets called multiple times each time new Tab is created. when tracing the code, the context variable gets null sometimes, and sometimes has the right value, which is the project I want to pass. but the it's always null at the end of calling the constructor.
So I'm not sure if this is the right way to do it, but it works.
First I've removed regionManager.RegisterViewWithRegion("ProjectExplorerRegion", typeof(ProjectExplorerView)); from ProjectViewModel to ShellViewModel, this was causing the view to be called multiple times as I have mentioned at the end of my question.
Second update the ParentProjectInfo implementation to use INotifyPropertyChanged, and inside the property setter, update what needs to be automatically updated.

Setting WPF datacontext for a specific control

I'm developing a WPF application and I'm struggling a little bit to understand some of the details of DataContext as it applies to binding. My application uses a business object which is defined like this:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// enumerations for some properties
public enum MyEnumValues
{
[Description("New York")]
NewYork,
[Description("Chicago")]
Chicago,
[Description("Los Angeles")]
LosAngeles
}
// an example property
private string _myPropertyName;
public string MyPropertyName
{
get { return _myPropertyName; }
set
{
if (_myPropertyName == value)
{
return;
}
_myPropertyName = value;
OnPropertyChanged(new PropertyChangedEventArgs("MyPropertyName"));
}
}
// another example property
private MyEnumValues _myEnumPropertyName;
public MyEnumValues MyEnumPropertyName
{
get { return _myEnumPropertyName; }
set
{
if (_myEnumPropertyName== value)
{
return;
}
_myEnumPropertyName= value;
OnPropertyChanged(new PropertyChangedEventArgs("MyEnumPropertyName"));
}
}
// example list property of type Widget
public List<Widget> MyWidgets { get; set; }
// constructor
public MyBusinessObject()
{
// initialize list of widgets
MyWidgets = new List<Widget>();
// add 10 widgets to the list
for (int i = 1; i <= 10; i++)
{
MyWidgets.Add(new Widget());
}
// set default settings
this.MyPropertyName = string.empty;
}
}
As you can see, I have some properties that are declared in this class one of which is a list of Widgets. The Widget class itself also implements INotifyPropertyChanged and exposes about 30 properties.
My UI has a combobox which is bound to my list of Widgets like this:
MyBusinessObject myBusinessObject = new MyBusinessObject();
public MainWindow()
{
InitializeComponent();
this.DataContext = myBusinessObject;
selectedWidgetComboBox.ItemsSource = myBusinessObject.MyWidgets;
selectedWidgetComboBox.DisplayMemberPath = "WidgetName";
selectedWidgetComboBox.SelectedValuePath = "WidgetName";
}
The majority of the controls on my UI are used to display the properties of a Widget. When my user selects a Widget from the combobox, I want these controls to display the properties for the selected Widget. I'm currently achieving this behavior by updating my window's DataContext in the SelectionChanged event handler of my combobox like this:
private void selectedWidgetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.DataContext = selectedWidgetComboBox.SelectedItem;
}
This allows me to bind my controls to the appropriate Widget property like this:
<TextBox Text="{Binding WidgetColor}"></TextBox>
However, not all of the controls in my UI are used to display Widget properties. Some of the controls need to display the properties from MyBusinessObject (for example: MyPropertyName defined above). In this scenario, I can't simply say:
<TextBox Text="{Binding MyPropertyName}"></TextBox>
...because the DataContext of the window is pointing to the selected Widget instead of MyBusinessObject. Can anyone tell me how I set the DataContext for a specific control (in XAML) to reference the fact that MyPropertyName is a property of MyBusinessObject? Thank you!
Instead of changing the DataContext of your window, you should add a property to your MyBusinessObject class like this one:
private Widget _selectedWidget;
public Widget SelectedWidget
{
get { return _selectedWidget; }
set
{
if (_selectedWidget == value)
{
return;
}
_selectedWidget = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedWidget"));
}
}
Then bind SelectedWidget to the SelectedItem property of your combobox. Anywhere that you need to use the widget's properties you can do this:
<TextBox Text="{Binding Path=SelectedWidget.WidgetColor}"></TextBox>
try
<TextBox Text="{Binding MyBusinessObject.MyPropertyName}"></TextBox>
this works if MyBusinessObject is the datacontext of the textbox and MyPropertyName is a property of MyBusinessObject
Also, Here is a good article to clarify binding
hope this helps
EDIT 1:
use a relative binding like this:
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfControl}}}"
So the relatve binding allows you to look up the visual tree to another UI element and use its datacontext. I would consider wrapping your window's contents in a grid. and wet your windows datacontext to the businessobject and the grids datacontext to the widget. That way you can always use the parent window's datacontext through the realtive source binding.
so use the following if your window's datacontext is your business object
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"

How to create silverlight 4 usercontrol with dependency property that can accept any type

I am looking for a way to create an UserControl in silverlight 4 and expose a dependency property, which can accept any type. What I mean by that is, for example, if you look at standard silverlight control like AutoCompleteBox, it is capable of handling any type of collections. So you can bind AutoCompleteBox with IEnumerable<Human> or IENumerable<Animal> etc. And when any item is selected AutoCompleteBox returns the selected value either Human instance or Animal instance via SelectedItem dependency property.
I want to achieve similar flexibility with my usercontrol. I wouild like to expose 2 dependency properties SuggestedItems and SelectedItem. Which ever collection is set to SuggestedItems via consumers of this usercontrol thru Binding, lets take as an example IEnumerable<Car>, the I want SelectedItem property to send instance of Car type back to consumer thru Binding. If I used IEnumerable<Boat>, then I need Boat to be returned with SelectedItem.
I was trying to achieve it by using below example using MVVM, but its not working. I am looking for some clues as to how it should be designed, Am I even on a correct path or I have to completely alter my design?
I created an UserControl called VehicleSelectorUserControl which has its own dedicated ViewModel called VehicleSelectorViewModel with two proerties SuggestedItems, SelectedItem.
And usercontrol has corresponding Dependency properties in its codebehind to expose them to consumers of usercontrol. UserControl XAML has a ListBox which is bound to SuggestedItems property of VehicleSelectorViewModel. When user makes a selection, VehicleSelectorViewModel SelectedItem is set, which them invokes a delegate called ItemSelected to notify VehicleSelectorUserControl codebehind, which then sets the SelectedItem Dependency property to make it available to consumer.
Below is code from the VehicleSelectorUserControl.xaml.cs code behind.
private VehicleSelectorViewModel _TheViewModel;
public UserNameControl()
{
InitializeComponent();
_TheViewModel = Resources["TheViewModel"] as VehicleSelectorViewModel;
_TheViewModel.ItemSelected = OnItemSelected;
}
public IEnumerable<object> SuggestedItems
{
get { return (IEnumerable<object>)GetValue(SuggestedItemsProperty); }
set { SetValue(SuggestedItemsProperty, value); }
}
public static readonly DependencyProperty SuggestedItemsProperty =
DependencyProperty.Register("SuggestedItems", typeof(IEnumerable<object>), typeof(VehicleSelectorControl), new PropertyMetadata(OnSuggestedItemsSet));
private static void OnSuggestedItemsSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_TheViewModel.SuggestedItems = e.NewValue;
}
public object SelectedItem
{
get { return (String) GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(VehicleSelectorControl), null);
private void OnItemSelected()
{
SelectedItem = _TheViewModel.SelectedItem;
}
Its ViewModel VehicleSelectorViewModel code
public Action ItemSelected { get; set; }
private dynamic _SelectedItem;
public dynamic SelectedItem
{
get { return _SelectedItem; }
set
{
if (value != _SelectedItem)
{
_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
if(ItemSelected != null) ItemSelected.Invoke();
}
}
}
private dynamic _SuggestedItems;
public dynamic SuggestedItems
{
get { return _SuggestedItems; }
set
{
if (value != _SuggestedItems)
{
_SuggestedItems = value;
NotifyPropertyChanged("SuggestedItems");
}
}
}
The XAML of consumer will look like (Consumer has its own ViewModel, which responsible for supplying SuggestedCars [IEnumerable<Car>], SuggestedBoats [IEnumerable<Boat>].
<my:VehicleSelectorControl x:Name="MyCarSelectorControl"
SuggestedItems="{Binding SuggestedCars, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedCar, Mode=TwoWay}" />
<my:VehicleSelectorControl x:Name="MyBoatSelectorControl"
SuggestedItems="{Binding SuggestedBoats, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedBoat, Mode=TwoWay}" />

WPF MVVM Bind list on custom control to ViewModel

Is it possible to bind data in the "wrong" direction? I want a value in a custom control to be bound to my ViewModel. I've tried binding with mode "OneWayToSource" but I can't get it to work.
Scenario (simplified):
I have a custom control (MyCustomControl) that has a dependency property that is a list of strings:
public class MyCustomControl : Control
{
static MyCustomControl()
{
//Make sure the template in Themes/Generic.xaml is used.
DefaultStyleKeyProperty.OverrideMetadata(typeof (MyCustomControl), new FrameworkPropertyMetadata(typeof (MyCustomControl)));
//Create/Register the dependency properties.
CheckedItemsProperty = DependencyProperty.Register("MyStringList", typeof (List<string>), typeof (MyCustomControl), new FrameworkPropertyMetadata(new List<string>()));
}
public List<string> MyStringList
{
get
{
return (List<string>)GetValue(MyCustomControl.MyStringListProperty);
}
set
{
var oldValue = (List<string>)GetValue(MyCustomControl.MyStringListProperty);
var newValue = value;
SetValue(MyCustomControl.MyStringListProperty, newValue);
OnPropertyChanged(new DependencyPropertyChangedEventArgs(MyCustomControl.MyStringListProperty, oldValue, newValue));
}
}
public static readonly DependencyProperty MyStringListProperty;
}
The control also contains code to manipulate this list.
I use this custom control in a UserControl that has a ViewModel. The ViewModel has a property that is also a list of strings:
public List<string> MyStringsInTheViewModel
{
get
{
return _myStringsInTheViewModel;
}
set
{
if (value != _myStringsInTheViewModel)
{
_myStringsInTheViewModel = value;
OnPropertyChanged("MyStringsInTheViewModel");
}
}
}
private List<string> _myStringsInTheViewModel;
Now I want to bind the list in my custom control (MyStringList) to the list in my ViewModel (MyStringsInTheViewModel) so that when the list is changed in the custom control it is also changed in the ViewModel. I've tried this but can't get it to work...
<myns:MyCustomControl MyStringList="{Binding Path=MyStringsInTheViewModel, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}">
How can I make such a binding?
Use ObservableCollection<T> instead of List<T>. It implements INotifyCollectionChanged Interface.

WPF Inner Property Binding not updating

I have an INotifyProperty Screen item that I have bound to a wpf control.
Ok... I Simplified everything and am posting more code. I have a MainViewModel with the selected screen property.
public Screen SelectedScreen
{
get { return this.selectedScreen; }
set
{
this.selectedScreen = value;
this.OnPropertyChanged("SelectedScreen");
}
}
I have a textbox that is bound to this property:
<TextBlock Text="{Binding Path=SelectedScreen.ScreenNumber}" />
This all works initially. I have created another control that is changing the selected screen with the following code.
public Screen SelectedScreen
{
get { return (Screen)GetValue(SelectedScreenProperty); }
set
{
this.SetValue(SelectedScreenProperty, value);
for (int x = 0; x < this.Screens.Count; ++x)
this.Screens[x].IsSelected = false;
value.IsSelected = true;
}
}
public ObservableCollection<Screen> Screens
{
get { return (ObservableCollection<Screen>)GetValue(ScreensProperty); }
set { this.SetValue(ScreensProperty, value); }
}
public static readonly DependencyProperty SelectedScreenProperty =
DependencyProperty.Register("SelectedScreen",
typeof(Screen),
typeof(ScreenSelection));
public static readonly DependencyProperty ScreensProperty =
DependencyProperty.Register("Screens",
typeof(ObservableCollection<Screen>),
typeof(ScreenSelection),
new UIPropertyMetadata(new ObservableCollection<Screen>()));
This screen selection control is working. When I change screens and put a breakpoint on the set property of SelectedScreen it is called which then calls the SelectedScreen property of the MainViewModel. So the event is firing, but the textbox isn't updated even though it binds correctly the first time.
Does the class which contains the SelectedScreen property implement INotifyPropertyChanged? When the SelectedScreen property changes, the containing class should raise the PropertyChanged event, and typically, WPF should update the Binding.
Thank you gehho for looking at this. I figured it out and there is no way you had enough information to be able too. I was inheriting from ViewModelBase in the MainViewModel that was inheriting from ObservableObject where I implemented INotifyPropertyChanged. The problem is that I implemented the methods for INotifyPropertyChanged in both classes and WPF was listening to the wrong one. Very obscure. Very annoying. Very lasjkdf;ashdoh

Resources