Programmatically triggering the HyperlinkButton navigation - silverlight

I'm using the Silverlight 3 HyperlinkButton and I want to programmatically trigger the navigation that normally occurs when the button is clicked. There does not appear to be a public method that supports this.
The OnClick method is protected so I could inherit from this control to gain access to this method from a custom control but is there a better way to go about this?
Looking at the implementation of the OnClick method using Reflector does not give me an obvious hint for a better way and the complexity of the code makes me wonder if there is a good reason for not allowing this event to be triggered programmatically. The lowest level managed code call is to MS.Internal.XcpImports.NavigateToSafeURINative and I don't see any public method in the code path that I could use.

For an in browser application, the HtmlWindow.Navigate does the trick as follows, relying on a unique target name to make sure it opens in a different tab or window to the current window hosting the silverlight application.
var hostingWindow = HtmlPage.Window;
hostingWindow.Navigate(siteHyperLinkbutton.NavigateUri, siteHyperLinkbutton.TargetName);
For an out of browser application, the best solution I have found is to derive a very simple custom class from HyperlinkButton and implement a public method that in turn invokes the protected OnClick method as shown below. This custom class can be declared in XAML with the appropriate NavigateUri and TargetName properties, just like the base HyperlinkButton. I was hoping to avoid creating a custom class by invoking the OnClick method via reflection but this not possible due to the Silverlight Security Considerations for Reflection.
public class CustomHyperlinkButton : HyperlinkButton
{
/// <summary>
/// Exposes the base protected OnClick method as a public method.
/// </summary>
public void OnClickPublic()
{
OnClick();
}
}

You could link the button to a Command, then trigger the command anywhere from Code using Command.Execute(). Psuedo code:
XAML:
<UserControl x:Class="ClassName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:commands="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation">
<Button commands:Click.Command="{Binding MyCommand}" />
</UserControl>
CODE (behind or MVVM):
public class ClassName
{
///Class constructor
public ClassName()
{ /// implement the command behaviour as a delegate
MyCommand = new DelegateCommand<object>(
delegate{
/// do your OnClick() behaviour implementation here
}
);
}
private DelegateCommand<object> _myCommand;
public DelegateCommand<object> MyCommand
{
get { return _myCommand; }
set { myCommand=value;
OnPropertyChanged("MyCommand");}
}
/// Another method called from somewhere else in code
void SomeOtherMethod()
{
MyCommand.Execute(null);
}
}
This works particularly well in MVVM world.

Related

WPF, MVVM IoC: Alternative to Service Locator Pattern. Need dependency in View code behind

Following several guides I have a application layout like below using WPF .NET 4.7.1 and MVVM-Light. I'm totally new to WPF btw.
App.xaml:
<Application x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodel="clr-namespace:My.ViewModel"
StartupUri="View\MainView.xaml">
<Application.Resources>
<ResourceDictionary>
<viewmodel:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
That registers the "ViewModelLocator" class as a resources and sets the WPF startup to "View/MainView.xaml".
MainView.xaml:
<Window x:Class="My.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
Where the ViewModelLocator is used like a Service Locator Pattern. Here setting the DataContext to my "MainViewModel" (not shown). As much as I do not like this, I can live with it in the WPF XAML context. However now it turns out that I need a dependency in the code-behind of the view (not the ViewModel).
MainView.cs:
public partial class MainView : INotifyPropertyChanged
{
public MainView()
{
// Need to access dependency here.
}
}
Now I could just call the ViewModelLocator directly in that constructor and have that resolve from my IoC container - but then I've completely given in and accepting that pattern.
I would prefer to have the dependency injected in the ctor of course, and if that is possible, I would also leave the ViewModelLocator entirely and inject the ViewModel here.
So question is, are there some standard way of instructing WPF application to use my container? And if yes, is it adviceable to go down that path and not use the ViewModelLocator thing?
You absolutely do not have to use the ViewModelLocator (Side note, the service locator pattern has had it's fair share of criticism lately as an anti-pattern, but I'll let you form your own opinion). MVVM Light and other Libraries basically give you access to a tool kit. You don't need to use all of the tools, and you should only use what is necessary for your specific domain.
Outside of the ViewModelLocator, there are two patterns known as ViewModel First and View First both have their pro's and cons. However both provide a means to decouple your code, which means it's not difficult to switch later.
As for constructing an application using MVVM Light without the service locator, my implementation of the View First method looks something like this.
I've heard the opinion that ViewModel First is preferred, however I find View First to be more simplistic for Test Driven Development (TDD)
App.xaml.cs (Application Code Behind)
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bootStrapper = new BootStrapper();
//Container Builder
var container = bootStrapper.BootStrap();
var mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
}
}
BootStrapper.cs (I'm using AutoFac in this case, but you can easily substitute.)
public class BootStrapper
{
public IContainer BootStrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<MainWindow>().AsSelf();
builder.RegisterType<MainWindowViewModel>().AsSelf();
return builder.Build();
}
}
MainWindowViewModel.cs
//I rolled my own ViewModelBase, but you can use MVVM Light's ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
public string DisplayProgram
{
get { return _displayProgram; }
//MVVM Light's ViewModelBase uses RaisePropertyChanged();
set { _displayProgram = value; OnPropertyChanged(); }
}
public void Initialize()
{
//Called from view code behind.
}
}
MainWindow.xaml.cs (MainWindow Code Behind)
//When MainWindow.Show()..
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _viewModel;
//Container resolves dependencies
public MainWindow(MainWindowViewModel viewModel)
{
//Let base Window class do its thing.
InitializeComponent();
//Handle loaded event
Loaded += MainWindowLoaded;
//Hold on to the MainWindowViewModel, and set it as the windows DataContext
_viewModel = viewModel;
DataContext = _viewModel;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
_viewModel.Initialize();
}
}

Caliburn.Micro - why uses UserControl instead of Window

My question is exactly like in the title.
I'm starting with Caliburn.Micro for MVVM approach (which also is new for me) and in every tutorial the first step is to remove the default MainWindow.xaml file and create a new UserControl file. Why is that? UserControl does not even accept a Title. Isn't it possible to build application using normal Windows? I already tried that, but with every launch I get error "Cannot find view for ViewModel", although both MainView.xaml and MainViewModel.cs are present. When I created a pair of USerControl and ViewModel for it, everything started to work as expected. So again, why Windows don't work?
It wouldn't really be a problem, but I'm thinking that some additions like Modern UI themes for WPF might not work without a window. I'm not sure of that.
Probably one solution would be to display a defined UserControl View inside of a Window, but it's just a workaround.
You could create your own custom shell window by creating a custom WindowManager:
public class CustomWindowManager : WindowManager
{
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
Window window = new Window();
window.Title = "custom...";
return window;
}
}
...that you register in your bootstrapper:
public class HelloBootstrapper : BootstrapperBase
{
...
protected override void Configure()
{
_container.Singleton<IWindowManager, CustomWindowManager>();
...
}
}

UI Binding Validation in MVVM

I'm working on converting some code to a more proper MVVM implementation using DataTemplates and am having problems with certain kinds of UI validation.
I've got no problems with validation in the View Models -- IDataErrorInfo is implemented and everything is fine. What I've got a problem with is UI binding errors where they might put letters in a TextBox bound to an int.
Previously, I used :
System.Windows.Controls.Validation.AddErrorHandler(userControl, handler)
... and kept a count of errors added and removed to know whether all the form's data was OK.
But now that I'm doing MVVM I don't have access to the userControl to set up this handler. So I don't really have a hook to get this started.
Is there some sort of global DataTemplateApplied event handler available where I could do something like:
void OnDataTemplateApplied(object data, Control template)
{
if (data is MyViewModelBase)
{
Validation.AddErrorHandler(template, handler);
}
}
Alternatively, maybe I can call AddErrorHandler once in the bootstrapper for the outer Shell window, and then each time the event is fired somehow figure out which ViewModel is powering that particular control?
I know some people like making all VM fields strings and doing lots of type conversion in the VM -- that's not going to be realistic for our system for a variety of reasons.
You might be interested in this answer: https://stackoverflow.com/a/13335971/1094526
The main idea is exactly what you said (subscribe to the error handler). As I understand, the problem is you don't have access to the control from the ViewModel, but it isn't hard to solve
In a project I'm working, I exposed two methods from my ViewModel: AddUIError and RemoveUIError. I create an event handler in my View and there I cast the DataContext to the type of my ViewModel and call AddUIError or RemoveUIError depending on what happened.
I am using DataTemplates to associate a View with a ViewModel, so when the template is applied, the DataContext is automatically set to the ViewModel. If you want, you can store your ViewModel in a private field (in the View) and update the reference each time the DataContext changed (there is a DataContextChanged event)
If this will be done in multiple ViewModels, you can put both methods (AddUIError and RemoveUIError) in a class like ViewModelBase and move the ValidationError event handling to a Behavior and use it in each view.
More info about the behavior part:
The Behavior class is part of the Expression Blend SDK, so you will need it if you want to follow this way.
Behaviors are useful to attach some common functionality to many components without creating derived classes, for example.
First, we need to define the AddUIError and RemoveUIError in a class named ViewModelBase (which is, of course, the base class for all other ViewModels):
class ViewModelBase {
public void AddUIError(...) {/* Details ommitted */ }
public void RemoveUIError(...) {/* Details ommitted */ }
}
Then, create a Behavior by subclassing Behavior. We use FrameworkElement as the template argument so this behavior can be attached to any FrameworkElement (or derived class) instance:
class NotifyDataErrorsBehavior : Behavior<FrameworkElement>
{
// Called when the the Behavior is attached
protected override void OnAttached()
{
base.OnAttached();
// Initialize the handler for the Validation Error Event
_handler = new RoutedEventHandler(OnValidationRaised);
// Add the handler to the event from the element which is attaching this behavior
AssociatedObject.AddHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
}
protected override void OnDetaching()
{
base.OnDetaching();
// Remove the event handler from the associated object
AssociatedObject.RemoveHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
}
private RoutedEventHandler _handler = null;
private void OnValidationRaised(object sender, RoutedEventArgs e)
{
var args = (System.Windows.Controls.ValidationErrorEventArgs)e;
ViewModelBase viewModel = AssociatedObject.DataContext as ViewModelBase;
if (viewModel != null)
{
// You can add only Exception validation errors if you want..
if (args.Action == ValidationErrorEventAction.Added)
viewModel.AddUIValidationError(...);
else if (args.Action == ValidationErrorEventAction.Removed)
viewModel.RemoveUIValidationError(...);
else
throw new NotSupportedException("ValidationErrorEventAction has changed");
}
}
}
And finally just use it in XAML:
1. Add a reference to the namespace where NotifyDataErrorsBehavior is located, and also a reference to System.Windows.Interactivity namespace (from Expression Blend SDK):
<UserControl
...
xmlns:behavior="clr-namespace:MyApp.Behaviors"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
>
2. Add the behavior (at the same level as the content of your UserControl:
<i:Interaction.Behaviors>
<behavior:NotifyDataErrorsBehavior/>
</i:Interaction.Behaviors>
Ex:
<UserControl
...
xmlns:behavior="clr-namespace:MyApp.Behaviors"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
>
<i:Interaction.Behaviors>
<behavior:NotifyDataErrorsBehavior/>
</i:Interaction.Behaviors>
<Grid>
...
</Grid>
</UserControl>

MVVM pattern violation: MediaElement.Play()

I understand that ViewModel shouldn't have any knowledge of View, but how can I call MediaElement.Play() method from ViewModel, other than having a reference to View (or directly to MediaElement) in ViewModel?
Other (linked) question: how can I manage View's controls visibility from ViewModel without violating MVVM pattern?
1) Do not call Play() from the view model. Raise an event in the view model instead (for instance PlayRequested) and listen to this event in the view:
view model:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
view:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2) You can expose in the view model a public boolean property, and bind the Visibility property of your controls to this property. As Visibility is of type Visibility and not bool, you'll have to use a converter.
You can find a basic implementation of such a converter here.
This related question might help you too.
For all the late-comers,
There are many ways to achieve the same result and it really depends on how you would like to implement yours, as long as your code is not difficult to maintain, I do believe it's ok to break the MVVM pattern under certain cases.
But having said that, I also believe there is always way to do this within the pattern, and the following is one of them just in case if anyone would like to know what other alternatives are available.
The Tasks:
we don't want to have direct reference from the ViewModel to any UI elements, i.e. the the MediaElement and the View itself.
we want to use Command to do the magic here
The Solution:
In short, we are going to introduce an interface between the View and the ViewModel to break the dependecy, and the View will be implementing the interface and be responsible for the direct controlling of the MediaElement while leaving the ViewModel talking only to the interface, which can be swapped with other implementation for testing purposes if needed, and here comes the long version:
Introduce an interface called IMediaService as below:
public interface IMediaService
{
void Play();
void Pause();
void Stop();
void Rewind();
void FastForward();
}
Implement the IMediaService in the View:
public partial class DemoView : UserControl, IMediaService
{
public DemoView()
{
InitializeComponent();
}
void IMediaService.FastForward()
{
this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
}
void IMediaService.Pause()
{
this.MediaPlayer.Pause();
}
void IMediaService.Play()
{
this.MediaPlayer.Play();
}
void IMediaService.Rewind()
{
this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
}
void IMediaService.Stop()
{
this.MediaPlayer.Stop();
}
}
we then do few things in the DemoView.XAML:
Give the MediaElement a name so the code behind can access it like above:
<MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
Give the view a name so we can pass it as a parameter, and
import the interactivity namespace for later use (some default namespaces are omitted for simplicity reason):
<UserControl x:Class="Test.DemoView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
x:Name="MediaService">
Hookup the Loaded event through Trigger to pass the view itself to the view model through a Command
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
</ia:EventTrigger>
</ia:Interaction.Triggers>
last but not least, we need to hookup the media controls through Commands:
<Button Command="{Binding PlayCommand}" Content="Play"></Button>
<Button Command="{Binding PauseCommand}" Content="Pause"></Button>
<Button Command="{Binding StopCommand}" Content="Stop"></Button>
<Button Command="{Binding RewindCommand}" Content="Rewind"></Button>
<Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button>
We now can catch everything in the ViewModel (I'm using prism's DelegateCommand here):
public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
public IMediaService {get; private set;}
private DelegateCommand<IMediaService> loadedCommand;
public DelegateCommand<IMediaService> LoadedCommand
{
get
{
if (this.loadedCommand == null)
{
this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) =>
{
this.MediaService = mediaService;
});
}
return loadedCommand;
}
}
private DelegateCommand playCommand;
public DelegateCommand PlayCommand
{
get
{
if (this.playCommand == null)
{
this.playCommand = new DelegateCommand(() =>
{
this.MediaService.Play();
});
}
return playCommand;
}
}
.
. // other commands are not listed, but you get the idea
.
}
Side note: I use Prism's Auto Wiring feature to link up the View and ViewModel. So at the View's code behind file there is no DataContext assignment code, and I prefer to keep it that way, and hence I chose to use purely Commands to achieve this result.
I use media element to play sounds in UI whenever an event occurs in the application. The view model handling this, was created with a Source property of type Uri (with notify property changed, but you already know you need that to notify UI).
All you have to do whenever source changes (and this is up to you), is to set the source property to null (this is why Source property should be Uri and not string, MediaElement will naturally throw exception, NotSupportedException I think), then set it to whatever URI you want.
Probably, the most important aspect of this tip is that you have to set MediaElement's property LoadedBehaviour to Play in XAML of your view. Hopefully no code behind is needed for what you want to achieve.
The trick is extremely simple so I won't post a complete example. The view model's play function should look like this:
private void PlaySomething(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
return;
// HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI.
this.Source = null;
this.Source = new Uri(fileUri);
}
Here is the Source property, nothing special about it:
#region Source property
/// <summary>
/// Stores Source value.
/// </summary>
private Uri _Source = null;
/// <summary>
/// Gets or sets file URI to play.
/// </summary>
public Uri Source
{
get { return this._Source; }
private set
{
if (this._Source != value)
{
this._Source = value;
this.RaisePropertyChanged("Source");
}
}
}
#endregion Source property
As for Visibility, and stuff like this, you can use converters (e.g. from bool to visibility, which you can find on CodePlex for WPF, SL, WP7,8) and bind your control's property to that of the view model's (e.g. IsVisible). This way, you control parts of you view's aspect. Or you can just have Visibility property typed System.Windows.Visibility on your view model (I don't see any pattern breach here). Really, it's not that uncommon.
Good luck,
Andrei
P.S. I have to mention that .NET 4.5 is the version where I tested this, but I think it should work on other versions as well.

adding custom routed event of user control to xaml of a window in wpf

I am little new to Command binding so this might be a trivial question to many. I know that we can add Command bindings in xaml of a window and give its correspondng property in viewmodel. This viewmodel will be given to the DataContext of the window. Something like the following
--app.xaml.cs
mainWindow.DataContext = viewModel;
-- xaml
lt;Button Grid.Row="1" HorizontalAlignment="Right" Margin="0,3,18,3" Name="button1" Width="110"
Command="{Binding LoadCommand}">_Load</Button>
-- viewmodel
/// <summary>
/// Gets the load command.
/// </summary>
/// <value>The load command.</value>
public ICommand LoadCommand
{
get
{
if (m_LoadCommand == null)
{
m_LoadCommand = new RelayCommand(param => CanLoad(), param => Load());
}
return m_LoadCommand;
}
}
Here the relaycommand is a class which implements ICommand interface. CanLoad() and Load() are the methods which will get executed for canexecute and execute action of the relaycommand respectively. This is the click event of the button which is handled.
I have a user control which has a custom routedevent registered in it and the user control is then used on a window. I am currently adding the event handler explicitly in code.
//hook up event listeners on the actual UserControl instance
this.ucCustomEvent1.CustomClick += new RoutedEventHandler(ucCustomEvent_CustomClick);
//hook up event listeners on the main window (Window1)
this.AddHandler(UserControlThatCreatesEvent.CustomClickEvent, new RoutedEventHandler(ucCustomEvent_CustomClick));
I dont want to hook up the routedevent explicitly in code but in the xaml in the similar way as in the button example. I have uploaded the working sample code here for your perusal.
I'm not sure I fully understand your question but I hope one of my answers below helps you out.
To attach a "direct" event handler in XAML, just do the following:
<c:MyUserControl x:Name="uc1" CustomClick="uc1_CustomClickHandler"/>
To hook up a handler for the (routed) event of one element (e.g. the CustomClick event in your example) to another element (e.g. the parent window):
<Window c:MyUserControl.CustomClick="ucCustomEvent_CustomClick"/>
Now, if you want to tie up an event in your UI to a Command in your ViewModel, you will need attached behaviors to do that. There are lots of frameworks around featuring different implementations of this. Here's one you can try out: http://sachabarber.net/?p=514. It will allow you to do something like the following in your code:
<c:MyUserControl local:CommandBehavior.RoutedEventName="MyCustomClick"
local:CommandBehavior.TheCommandToRun="{Binding MyViewModelCommand}"/>
Hope this helps.

Resources