I have a page that can contain 4 different user controls, but only one of them should be visible at a time. I would like to do something like this:
<regControls:MyUserControl1 Visibility="{Binding Ctrl1, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl2 Visibility="{Binding Ctrl2, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl3 Visibility="{Binding Ctrl3, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl4 Visibility="{Binding Ctrl4, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
But when the component is initialised the converter isn't triggered at all. Even though I bind the property and raise execute change.
I would prefer not to initialise the different controls in the code behind. I'm using WP8 Silverlight (NOT WP 8.1). What am I missing?
EDIT:
I'm beginning to understand the problem. But I don't know how to solve it. We're using GWT so I get a call from GWT layer that tells me what to show:
public void ShowMyUserControl1()
{
SimpleIoc.Default.Register<MyPageViewModel>();
SimpleIoc.Default.Register<MyUserControl1ViewModel>();
SimpleIoc.Default.Register<MyUserControl2ViewModel>();
SimpleIoc.Default.Register<MyUserControl3ViewModel>();
SimpleIoc.Default.Register<MyUserControl4ViewModel>();
navigationService.NavigateTo(typeof(MyPageViewModel).FullName);
var viewModel = SimpleIoc.Default.GetInstance<MyPageViewModel>();
viewModel.DisplayMyUserControl1();
}
public void ShowMyUserControl2()
{
....
}
The problem here is that the view model is called and properties triggered before the components are initialised. The problem here is also that I don't know which method is called from GWT. It could be ShowMyUserControl1 or ShowMyUserControl2 (or method requesting control 3 or 4 to be shown. So somehow I need to tell this and at the same time pass parameters to the correct view model with arguments coming from GWT. Messenger (MVVMLight) doesn't work here. Because the message is sent before any control has been initialised and registered to listen to these messages. The user can have left the app and turn off the phone and then come back again, so I need to expect that the view models doesn't exist and needs to be registered.
Is DataContext (ViewModel) support INotifyPropertyChange?
Any binding errors in console output in VS?
Create breakpoint on property. Does it hint?
Related
I have developed a custom WPF control:
public partial class PercentTextbox : UserControl, IDataErrorInfo, INotifyDataErrorInfo
And I put that control inside a UserControl along with some other controls:
<UserControl x:Class="UserControlContainingPercentTextboxAndStuff" DataContext="Something" ...>
<Grid>
<mycontrols:PercentTextbox Value="{Binding MyPercentageValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}" ... />
<TextBox ... />
<mycontrols:PercentTextbox ... />
<TextBox ... />
<TextBox ... />
</Grid>
</UserControl>
And finally, I use another wrapping UserControl to show the above UserControl as dialog:
<UserControl ...>
<Grid>
<local:UserControlContainingPercentTextboxAndStuff ... />
<Button x:Name="SaveButton" Content="Save" ... />
<Button x:Name="CancelButton" Content="Cancel" ... />
</Grid>
</UserControl>
In the code behind of the latter, I want to subscribe to all validation errors, and disable the save button if there are errors.
Validation.AddErrorHandler(this, (sender, e) =>
{
SaveButton.IsEnabled = false;
Debug.WriteLine(e.Error);
});
I was thinking, that if I'd implement IDataErrorInfo or INotifyDataErrorInfo, WPF would somehow magically handle stuff for me, and create a ValidationError event (which would bubble up to the UserControl. But clearly, I am missing something essential here.
My question is: What do I have to implement in my custom control PercentTextbox in order to use it in arbitrary places and still get some kind of bubbling-up validation information which I can use in a container UserControl (e.g. to disable the SaveButton).
The IDataErrorInfo and INotifyDataErrorInfo are supposed to be implemented on the model side, not on the UI side. Then you can set the ValidatesOnDataErrors = True or ValidatesOnNotifyDataErrors = True options on your Bindings, so that the binding validation system jumps in. There are some good tutorials on the web about that. It's not the UI telling that something is invalid, but the data this UI represents.
The data validation concept is tightly coupled with the data bindings. If you want your user control to perform its own "UI" validation, use the coercing and validation callbacks of the dependency properties. However, this has nothing to do with the data validation of the binding system. The validation callback will cause the property system to throw an exception that you can handle as you wish (e.g. you can use the ExceptionValidationRule for your bindings).
Take a look on the Validation.Error attached event documentation (which you're actually trying to observe by calling Validation.AddErrorHandler). It states:
Occurs when the bound element runs into a validation error, but only
for bindings with the NotifyOnValidationError value set to true.
So you have two options now:
implement the validation on the model side and set up your bindings accordingly (you have to do this for each binding to your custom control's properties)
use the dependency property validation callbacks
I am using PRISM to auto-wire my Views & ViewModels, however I have encountered a problem I cannot solve.
I am using a calendar control, which enables users to create new appointments via opening new modal window & saving it to calendar.
This window, is styled via a ControlTemplate, where I have the following item:
<telerik:RadComboBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="3"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.EmployeeList}">
Items Source of this combobox is the ViewModels DataContext.EmployeeList - ObservableCollection<Employee>.
This would work like a charm as long as it would not be a new pop-out window. That way, I believe it is a userControl as well and therefore my regular code does not recognize any EmployeeList.
There might be 2 ways how to solve it (I don't have direct access to the modal window as it is being automatically generated by the control itself - I am using Telerik suite).
1) Make sure that the ItemsSource will dig deeper than the very first UserControl that it finds. Maybe by slightly changing the code, it will be able to do so? (Maybe using something like AncestorLevel...?).
2) Telerik has shown an example of how to achieve that by the following line:
<local:ViewModel x:Key="ViewModel" /> -- define key first
ItemsSource="{Binding Source={StaticResource ViewModel}, Path=EmployeesSource}"...
BUT the issue with my ViewModel is that under constructor I am passing several interfaces like following:
private readonly IEmployeeRepository _employeeRepository;
public EmployeeView_HolidaysViewModel(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
InitializeCollections();
InitializeCommands();
}
and therefore I can't make the above solution to work at all.
Any help with my problem would be highly appreciated. I simply need to get that list to that modal window's combobox.
In the end I managed to solve the problem by creating additional constructor to my class which looks like following:
public EmployeeView_HolidaysViewModel()
{
_employeeRepository = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IEmployeeRepository>();
InitializeCollections();
}
This way I can easily adopt Solution Nr 2 from the OP.
I have made quite a bit of progress on my first MVVM WPF application, the issue I am now having is I have a Window that has a viewmodel. This window has a button which opens another window that has another viewmodel.
Imagine a textbox on the first window. Once the second is opened the user will select a value and click save, this window will close and update the first window with its value.
When pushing save I have an ICommand on the childwindows Viewmodel that calls the SaveMethod. I have the selected value stored in a property on the Child windows viewmodel. But how do I update the Main Windows textbox with this value? I imagine I bind a property on the main windows view model, but unsure on how to continue.
Please advise, I can provide code examples if needed, but I think I may have explained it well enough, oh and thanks to everyone at StackOverflow for the help on my questions I have learnt a lot.
This is pretty straightforward using the MVVM Light framework. For the purposes of demonstration I'm going to use a string as the value you're passing, but it's easy to construct a different message type for whatever you need to pass.
In the constructor of your first Window's ViewModel you register to receive NotificationMessages. NotificationMessages are used to send string messages:
public MyFirstViewModel()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
In the SaveMethod in your second Window's ViewModel you send a message with the value you want to pass. I'm using MyStringValue as the name of the property that stores the value chosen by the user in your second Window:
private void SaveMethod()
{
MessengerInstance.Send(new NotificationMessage(MyStringValue));
}
When that message is received by the ViewModel of the first Window the NoitificationMessageReceived method is called. I'm going to put that value in a string property on the first ViewModel called MySavedValue:
private void NotificationMessageReceived(NotificationMessage msg)
{
MySavedValue = msg.Notification;
}
In your View for the first Window you have a TextBox with its Text property bound to MySavedValue. This updates whenever MySavedValue is updated.
In the parent viewmodel, you'll need a reference to the child viewmodel. When the child window is closed, you'll want to get the value of the secondviewmodel's property and set it to a appropriate property of the first parent viewmodel.
One of the posible (and simple) solutions is to keep one ViewModel for both windows
<Grid>
<StackPanel>
<TextBox Text="{Binding TheText}" />
<Button Command="{Binding ShowOptionsCommand}" Content="..."/>
</StackPanel>
<Popup IsOpen="{Binding IsShowingOptions}">
<StackPanel>
<ListBox ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption,Mode=TwoWay}"/>
<Button Command="{Binding SaveOption}">Save</Button>
</StackPanel>
</Popup>
</Grid>
//ShowOptionsCommand handler
void ShowOptions()
{
IsShowingOptions = true;
}
//SaveOptionCommand handler
void SaveOption()
{
TheText = SelectedOption;
IsShowingOptions = false;
}
I'm using the Popup to simplify the example.
Personally I'd go with the mvvm light framework already mentioned, but another option is to leverage IOC, also included with the above framework.
With this pattern view models have interfaces and are bound as properties from a view model locator data source. Within that, the child view model can be injected to the parent view model. Because IOC can create singleton instances of objects, the same instance gets given to the parent as is bound to the child window. That way you get a reference to the view model but through an interface thus preserving the separation.
Just offering this as an alternative technical solution beyond those already offered.
How to focus a textbox from ViewModel wpf?
<TextBox Name="PropertySearch"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Path=PropertySearch,
ValidatesOnDataErrors=True}"
Width="110"
Height="25"
Margin="10" />
You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.
You can do this with a DataTrigger.
View:
<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBox Name="PropertySearch" Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>
ViewModel:
// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;
The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.
Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.
Happy coding!
The question you should be asking yourself is "why does my ViewModel need to know which control has the focus?"
I'd argue for focus being a view-only property; it's an interaction property, and has nothing to do with the conceptual state. This is akin to the background color of a control: why would you represent it in the VM? If you need to manage the focus in a custom way, it's probably better to use a view-level object to do the job.
In your parent control, add the following property:
FocusManager.FocusedElement="{Binding ElementName=PropertySearch}"
While purists may argue for leaving this out of the VM, there are cases where it may make sense to do so from the VM.
My approach has been to make the view implement an interface, pass that interface to the ViewModel, and then let the VM call methods on the interface.
Example:
public interface IFocusContainer
{
void SetFocus(string target);
}
A couple things to keep in mind:
A VM might serve more than one instance of a view, so your VM might want to have a collection of references to IFocusContainer instances, not just one.
Code the VM defensively. You don't know whether there are 0, 1 or 20 views listening.
The "target" parameter of SetFocus() should probably be "loosely" coupled to the VM. You don't want the VM caring about the exact control names in the UI. Rather, the VM should indicate a name that is defined solely for focus management. In my case, I created some attached properties that would allow me to "tag" controls with "focus names".
To implement the interface, you can:
Implement it in the code-behind
Create some behaviors that know how to attach to the ViewModel that is present in the DataContext.
There's nothing wrong with implementing it on the Code Behind, but the behavior approach does allow a XAML only hookup if that's important to you.
In the implementation of the interface, you can use the visual tree to locate the control, or you could just code up a switch statement for a known set of focusable items.
I'm facing some problems binding a CommandParameter to its own Command in an application built using Prism 2.2 as MVVM . Let me introduce what it's happening.
I've got a customized listbox with a property named NumPageElements, and a couple of buttons to scroll through the list who needs that property. A simplified xaml of what I need (and works) in wpf is:
<Button x:Name="PageDownButton" Command="{Binding PageDownCommand}" CommandParameter="{Binding ElementName=ItemsListBox, Path=NumPageElements}" />
<Custom:MyOwnListBox x:Name="ItemsListBox" x:NumPageElements="{Binding ElementsPerPage, Mode=OneWayToSource}" >
. . .
</Custom:MyOwnListBox>
To have the same behaviour in Silverlight I wrote this xaml:
<Button Name="PageDownButton" Command="{Binding PageDownCommand}" CommandParameter="{Binding ElementName=ItemsListBox, Path=NumPageElements}" />
<Custom:MyOwnListBox Name="ItemsListBox" NumPageElements="{Binding Path=ElementsPerPage, Mode=TwoWay, UpdateSourceTrigger=Explicit}" >
. . .
</Custom:MyOwnListBox>
PageDownButton is an IApplicationCommand, ElementsPerPage is a property exposed by the presenter.
Now, the first time I open this view the buttons made in that way look enabled but they aren't 'clickable'. If I switch to a different view and I go back to the view with those button, they finally catch the correct behavior. It looks like it doesn't initialize correctly the first time the condition of the command (infact they should be disabled until I insert an item in the listbox), as if the parameter given via the CommandParameter property isn't initiliazed correctly. But I can't understand why switching between the views make it works.
I suspected I should force the UpdateSource of the bindings (I did it for ItemsListBox.NumPageElements and for PageDownButton.CommandParameter) after the view has been loaded, but doing it in the code behind was not of any help.
What I am doing wrong?
Thanks for any reply,
Mat.