C# Question

Binding of event in DataTemplate of WPF's TabItem's WebBrowser, MVVM

Question is: How to bind any event of WebBrowser to ICommand property in my View Model inside of ItemTemplate?

When i am trying to do this using the Expression blend interactivity libraries in normal for MvvmLight way, an exeption ocurs:


Error Collection property
'Microsoft.VisualStudio.DesignTools.WpfDesigner.InstanceBuilders.HwndHostInstance'.'Triggers'
is null.


WebTabItems is observable collection of items ViewModels

Here is code:

<TabControl ItemsSource="{Binding WebTabItems}" SelectedItem="{Binding SelectedWebTabItem}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SelectionChangedVMCommand}" PassEventArgsToCommand="True"></mvvm:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>

<TabControl.ItemTemplate>
<!--header-->
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>

<TextBlock Text="{Binding NotificationRibbonText}" Visibility="{Binding NotificationRibbonVisibility}"></TextBlock>


<WebBrowser Grid.Row="1" Visibility="Visible" local:WebBrowserExtension.BindableSource="{Binding Sourse}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigating">
<mvvm:EventToCommand Command="{Binding NavigatingMVCommand}" PassEventArgsToCommand="True" ></mvvm:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
</WebBrowser>



</Grid>
</DataTemplate>
</TabControl.ContentTemplate>

</TabControl>


Event binding in tab control works great but in template does not

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigating">
<mvvm:EventToCommand Command="{Binding NavigatingMVCommand}" PassEventArgsToCommand="True" ></mvvm:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>


Ps May be the problem is that WebBrowser.Navigating is not routed event, but problem is the same, How to bind to it event?

Answer

Have foud an answer how to bind event in template or when using Expression blend interactivity libraries is impossible

Attached property of ICommand type is other way through which you can achieve the same functionality.

This answer can also be used for binding to not Routed events

In my case: XAML

  <WebBrowser Grid.Row="1" Visibility="Visible" 
                                        local:WebBrowserExtension.BindableSource="{Binding NavigeteToSourse}" 
                                        local:WebBrowserExtension.NavigatingCmdExtended="{Binding NavigatingMVCommand}" 

                                        >

                            </WebBrowser>

separated class to add attached dependency properties to original web browser

 class WebBrowserExtension
    {

    #region BindableSourceProperty

    public static readonly DependencyProperty BindableSourceProperty =
    DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserExtension), new UIPropertyMetadata("", BindableSourcePropertyChanged));

    public static string GetBindableSource(DependencyObject obj)
    {
        return (string)obj.GetValue(BindableSourceProperty);
    }

    public static void SetBindableSource(DependencyObject obj, string value)
    {
        obj.SetValue(BindableSourceProperty, value);
    }

    public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        WebBrowser browser = o as WebBrowser;
        if (browser != null)
        {
            string uri = e.NewValue as string;
            browser.Source = !String.IsNullOrEmpty(uri) ? new Uri(uri) : null;
        }
    }
    #endregion

        #region NavigatingCmdExtended

        public static ICommand GetNavigatingCmdExtended(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(NavigatingCmdExtendedProperty);
        }
        public static void SetNavigatingCmdExtended(DependencyObject obj, ICommand value)
        {
            obj.SetValue(NavigatingCmdExtendedProperty, value);
        }
        // Using a DependencyProperty as the backing store for CalenderOpenCommand. This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NavigatingCmdExtendedProperty =
        DependencyProperty.RegisterAttached("NavigatingCmdExtended", typeof(ICommand), typeof(WebBrowserExtension), new PropertyMetadata(OnChangedNavigatingCmdExtendedProperty));


        private static void OnChangedNavigatingCmdExtendedProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var webBrowser = d as WebBrowser;
            if (webBrowser != null)
            {
                if (e.NewValue != null)
                {
                    //attach event handler
                    webBrowser.Navigating += webBrowser_Navigating;
                }
                else
                {
                    //detach event handler
                    webBrowser.Navigating -= webBrowser_Navigating;
                }
            }
        }
        ///
        /// Event handler for Calender Opened event.
        ///
        ///
        ///
        static void webBrowser_Navigating(object sender, NavigatingCancelEventArgs e)
        {
            ICommand command = GetNavigatingCmdExtended(sender as DependencyObject);
            if (command != null)
            {
                if (command.CanExecute(e))
                {
                    //executes a command
                    command.Execute(e);
                }
            }
        }
        #endregion

and command in ViewModel

public class WebTabItemVievModel: ViewModelBase
{

    public WebTabItemVievModel()
    {

        NavigatingMVCommand = new RelayCommand<NavigatingCancelEventArgs>(NavigatingMethod);

    }



    public ICommand NavigatingMVCommand { get;  set; }
    private void NavigatingMethod(NavigatingCancelEventArgs e)
    {
        Messenger.Default.Send<NotificationMessage <UriChangedMSG>>(new NotificationMessage<UriChangedMSG> (new UriChangedMSG { NewUri = e.Uri.AbsoluteUri },"test"));
        CurrentUri = e.Uri.AbsoluteUri;
        NotificationRibbonText = e.Uri.AbsoluteUri;
    }

Refer this article for details:

http://www.codeproblem.com/articles/frameworks/wpf/87-event-to-command-binding-using-attached-properties-in-plain-wpf-without-any-extra-dependancy?showall=1&limitstart=