Search code examples
c#wpfwinformswindowsformshost

Question about using the WindowsFormsHost. (with DataBinding)


My English skill is poor because I'm not a native English speaker. I hope you to understand.

I need to use the WindowsFormsHost control because using the DataGridView of the WinForm. I can't DataGrid control in WPF because of some reasons.

In the .cs file, I succeeded using the WindowsFormsHost with the DataGridView of the WinForm. The code is as shown below.

var tabItem = new ClosableTab();


#region Core Logic
var winformControl = new WindowsFormsHost();
winformControl.VerticalAlignment = VerticalAlignment.Stretch;
winformControl.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;

winformControl.Child = new DataGridView();
DataGridView parsingTableView = winformControl.Child as DataGridView;
parsingTableView.EditMode = DataGridViewEditMode.EditProgrammatically;
parsingTableView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
parsingTableView.DataSource = mainWindow.editor.TokenTable;
parsingTableView.CellMouseEnter += new DataGridViewCellEventHandler(this.tableGridView_CellMouseEnter);

tabItem.Content = winformControl;
#endregion


this.mainWindow.tablControl.Items.Add(tabItem);

Now I want to convert the above logic to the XAML so I wrote the logic as shown below.

First, I defined DataTemplate to display the DataTable type.

// In resource file

xmlns:systemData="clr-namespace:System.Data;assembly=System.Data"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"

<DataTemplate DataType="{x:Type systemData:DataTable}">
    <WindowsFormsHost>
        <WindowsFormsHost.Child>
            <wf:DataGridView DataSource="{Binding}" EditMode="EditProgrammatically" AutoSizeColumnsMode="AllCells"/>
        </WindowsFormsHost.Child>
    </WindowsFormsHost>
</DataTemplate>

Now the above DataTemplate is used when binding to Content as shown the below code.

// In xaml file

<Grid>
    <TabControl ItemsSource="{Binding SelectedItem}">
        <TabItem Header="{lex:Loc Key=TokenTable}" Content="{Binding TokenTable}"/>
    </TabControl>
</Grid>

When I execute the above code, I faced an error as below at the DataSource="{Binding}".

enter image description here

If I have to translate the above error using my poor English skill. The error tells me. "It can't configure 'Binding' on the 'DataSource' property." "The 'Binding' can configure only on the DependencyProperty of the DependencyObject."

I think I know what error trying to say to me but I don't know what I do to solve the above problem.

What should I do to solve this problem?

Thank you for reading.


Solution

  • You can't bind directly to the DataSource property of a DataGridView because it's not a dependency property.

    What you can do is to work around this by create an attached property that you set on the WindowsFormsHost as suggested here, and then set the property of the DataGridView using the callback of the attached property:

    public static class WindowsFormsHostMap
    {
        public static readonly DependencyProperty DataSourceProperty
            = DependencyProperty.RegisterAttached("DataSource", typeof(object), 
                typeof(WindowsFormsHostMap), new PropertyMetadata(OnPropertyChanged));
    
        public static string GetText(WindowsFormsHost element) => (string)element.GetValue(DataSourceProperty);
    
        public static void SetText(WindowsFormsHost element, object value) => element.SetValue(DataSourceProperty, value);
    
        static void OnPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var dataGridView = (sender as WindowsFormsHost).Child as DataGridView;
            dataGridView.DataSource = e.NewValue;
        }
    }
    

    XAML:

    <WindowsFormsHost local:WindowsFormsHostMap.DataSource="{Binding}">