What are people using instead of IMultiValueConverters in Windows 8? - xaml

I'm porting an existing WPF application to a Windows 8 application.
In the WPF application we make considerable use of MultiValue convertors to allow us to create values that are combinations of UI element properties and viewmodel properties (the weight of a hippo object and the actualWidth of an itemscontrol) to achieve nice UI effects.
Windows 8, however, doesn't have a MultiValue convertor.
Since I'm porting an application I don't really want to significantly change my viewmodels or Xaml.
How can I replicate Multivalue controller functionality with a minimum amount of pain and rewriting?

My approach was to expose a static singleton instance of the VM; for example:
public class MainViewModel : INotifyPropertyChanged
{
private static MainViewModel _mvm;
public static MainViewModel MVM
{
get
{
if (_mvm == null)
_mvm = new MainViewModel();
return _mvm;
}
}
Then simple pass the whole VM through:
<Button Command="{Binding MyCommand}" CommandParameter="{Binding MVM}">Run Command</Button>
It's not multibinding, but it does allow you to pass multiple parameters.

Related

Prism MVVM Binding to User Control Dependency Property

I have a Universal app that I'm writing and I want to use Prism and Unity as my MVVM framework. Everything was going great until I got to a view where I have multiple instances of the same user control(a custom Watermark Textbox). For some reason, I haven't been able to find a good solution to my problem. I imagine I'm overlooking something and there is a straightforward answer.
Here's my source code(just the relevant portions). Some background, I had this working before implementing Prism. The Commands attached to the user controls are firing as expected but I can't figure out how to manipulate the control itself):
Any guidance on how to use user controls with Prism or binding to dependency properties with Prism would be great. Thanks.
My View
<!-- I want to be able to set the watermark and also retrieve the text from my ViewModel -->
<uc:WatermarkTextBox Width="250"
x:Name="FullName">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=FullName}"/>
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
<!-- This one as well -->
<uc:WatermarkTextBox Width="250"
x:Name="EmailAddress">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=EmailAddress}" />
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
Going from your comments/error message ("Failed to assign to property %0") when attempting to bind the property directly, the Property you are trying to bind to isn't a dependency property.
Your WatermarkTextBox only implements a CLR Property, like this:
public class WatermarkTextBox : TextBox {
public object Watermark { get; set; }
}
This can't be used for databinding.
You need to implement it as Dependency Property, like
public class WatermarkTextBox : TextBox {
public object Watermark
{
get { return (object)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkTextbox), new PropertyMetadata(null));
}
This will allow to use <uc:WatermarkTextBox Watermark={Binding watermarkText, Mode=TwoWay} for example.
If you can't modify WatermarkTextBox (third-party closed source control), then you'll have to implement an attached behavior and then bind your ViewModel Property to the attached behavior and assign the Watermark property from within it.
If you need TwoWay binding (which is unlikely in case of Watermark property, since it won't be changed from UI), you have to register an event handler (KeyDown in linked answer) to this specific event and pass the new value to the attached property
Except these two ways, there is no way to update a Control's CLR property without violating MVVM.

passing view model from page to user control in windows store app

In a Windows Store split app, I want to pass a view model from a page to a user control. The scenario is that I want to reuse some common xaml in multiple pages, using a UserControl like a view.
In the main page:
<common:LayoutAwarePage
...
DataContext="{Binding ViewModel, RelativeSource={RelativeSource Self}}"
... >
<views:MyUserControlView Model="{Binding ViewModel}" />
...
In the user control code:
public sealed partial class MyUserControlView : UserControl
{
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(MenuSource),
typeof(MyUserControlView), null);
...
public ModelType Model
{
get
{
return this.GetValue(ModelProperty) as ModelType ;
}
set
{
this.SetValue(ModelProperty, value);
}
}
The Model setter is never called. How do I hook up the user control to the parent page's view model?
Or, is there a better way to implement shared views for use in pages?
Thanks.
-John
Correct binding would be:
<views:MyUserControlView Model="{Binding}" />
You've already set DataContext for the page above. All bindings are relative to the current DataContext.
The setter still won't be called, though. It is just a wrapper to access the DependencyProperty from code. Binding will call SetValue directly.
Depending on your requirements you might not even need to define your own Model DependencyProperty. Each control automatically inherits DataContext from its parent control. In your example above the user control already has its DataContext set to the same view model as the page.

Declaring ViewModel using Constructor Injection in XAML

I'm trying out Unity and I'm having problems declaring my viewmodel in XAML. Can you help me out?
XAML:
<UserControl.DataContext>
<search:SearchBoxViewModel />
</UserControl.DataContext>
Constructor:
[ImportingConstructor]
public SearchBoxViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
{
this.regionManager = regionManager;
this.eventAggregator = eventAggregator;
}
When I try to execute I get a resolutionfailedexception.
This worked when the viewmodel had an empty constructor. It seems as if it's having problems with the constructor injection.
If I load the module like this:
var searchView = Container.Resolve<SearchBoxView>();
searchView.DataContext = Container.Resolve<SearchBoxViewModel>();
//RegionManager.RegisterViewWithRegion(RegionNames.SearchRegion, typeof(SearchBoxView));
RegionManager.Regions[RegionNames.SearchRegion].Add(searchView);
It works.
Is there any possibility to do this with xaml ( with I personally think is better )?
By the way: I'm creating an application with wpf that primarily communicates with a webservice. What should I rather user: unity or MEF and what are the big differences between the two?
Thanks,
Raphi
http://msdn.microsoft.com/en-us/library/ms753379.aspx:
Requirements for a Custom Class as a XAML Element
In order to be able to be instantiated as an object element, your
class must meet the following requirements:
Your custom class must be public and support a default
(parameterless)
public constructor. (See following section for notes regarding
structures.)
...
So, if you want to use dependencies, you should right something like:
var searchView = Container.Resolve<SearchBoxView>();
public class SearchBoxView: UserControl
{
[Dependency]
public SearchBoxViewModel ViewModel
{
get { return (SearchBoxViewModel)DataContext; }
set { DataContext = value; }
}

NInject: Send parameter to ViewModel Class Constructor

I am developing a Windows Phone 7 app and am using the MVVM pattern. I have a need to pass a parameter to the contructor of the ViewModel for a page. All my datacontexts and binding are done in XAML. Through my research I've seen that I need to do so using a dependency injector such as NInject.
Here's a little detail on whats going on:
I have a page with a ListPicker that lists various tasks. Each task has a unique TaskID. When an item is selected I need to open another page that will show the selected Tasks detail. My ViewModel and binding is all done and works if I use a static TaskID in the ViewModel but of course I need to use a variable.
I've setup NInject in the project and the various classes needed such as ViewModelLocator and my NInjectModule as shown here:
public class LighthouseNInjectModule : NinjectModule
{
public override void Load()
{
this.Bind<TaskViewModel>().ToSelf().WithConstructorArgument("TaskID", 2690);
}
}
Note that I have hardcoded a TaskID here and using this code this value properly gets injected into my constructor. Of course, this is hardcoded and I need to get the TaskID for the selected ListPicker item. I know how to get the selected ID from the ListPicker but how do I make NInject aware of it so when my class constructor is run it will have the correct value?
Here is the basic definition of my ViewModel class showing use of the Injector attribute.
public class TaskViewModel : INotifyPropertyChanged
{
[Inject]
public TaskViewModel(int TaskID)
{
//run function to get data using TaskID
}
}
WithConstructorArgument has another oveload that accepts a lazy evaluated Func<Context, object>.

Getting Unity to Resolve views in XAML

I'm starting out with MVVM, and I'm starting to understand things. I'm currently experimenting with the Cinch framework, though I'm not committed to it as of yet.
I was injecting the ViewModels into the Views using by having a reference to the ViewModel in the codebehind of the view, with the property having a [Dependency] on it, and in the setter it sets the DataContext to the right view, using Unity. Neat trick, I thought.
I'm trying to get my app to work as a single Window, with injected views (As opposed to multiple windows and dealing with opening\closing them)
I changed my views from Windows to UserControls, and added a to the main window.
That worked, but the ViewModel was never injected, presumably because the XAML doesn't use Container.Resolve to create the view, as when I created the view and added it manually in the code-behind using Resolve, the [Dependency] was created.
How can I set up my window, so that if I add a view through XAML, or the view gets changed as a result of a UI action etc, it gets it through Unity, so that it can work its magic?
This problem is normally solved using Regions and the RegionManager. In the main window ViewModel, a set of Regions is created and added to the RegionManager. Then ViewModels can be Resolved and added to the Region.Views collection.
In XAML, the Region is normally injected by having the ItemsSource property of an ItemsControl bound to the region property of the main ViewModel.
So, in the main screen ViewModel you would have something like this:
public class TestScreenViewModel
{
public const string MainRegionKey = "TestScreenViewModel.MainRegion";
public TestScreenViewModel(IUnityContainer container, IRegionManager regionManager)
{
this.MainRegion = new Region();
regionManager.Regions.Add(MainRegionKey, this.MainRegion);
}
public Region MainRegion { get; set; }
}
This would be Resolved normally in your IModule
#region IModule Members
public void Initialize()
{
RegisterViewsAndServices();
var vm = Container.Resolve<SelectorViewModel>();
var mainScreen = Container.Resolve<TestScreenViewModel>();
mainScreen.MainRegion.Add(vm);
var mainView = ContentManager.AddContentView("Test harness", mainScreen);
}
#endregion
And the XAML representation of your template looking something like
<DataTemplate DataType="{x:Type TestModule:TestScreenViewModel}">
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=MainRegion.Views}" />
</StackPanel>
</ScrollViewer>
</DataTemplate>
The way to solve your problem is to make your window to have a ViewModel as well, with ViewModels of UserControls exposes as properties on it. Then in your XAML for a window you'd simply use Binding mechanism to bind UserControl's DataContexts to proper properties of your your main ViewModel. And since that main ViewModel is resolved from Unity container it would have all other ViewModel-s injected as needed.