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?

Answers


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=


Need Your Help

User-mode synchronization library for C++

c++ multithreading synchronization

Does anyone know of a Windows user-mode thread synchronization library for C++ (utilizing spin locks / atomic operations)? I only need mutexes (~critical sections), but condition variables would be a

How to translate a set of points in a UIBezierPath or CGPath in Swift for iOS?

ios swift core-graphics uibezierpath cgpath

I've created a CGPath from a glyph using CoreText's CTFontCreatePathForGlyph. Thanks to this SO post, I also have access to all the CGPoint in my CGPath as an array, which I can .filter() and .map(...