Search code examples
c#wpfflowlayoutpanelwindowsformshostonmousemove

Mouse move event is not working inside WindowsFormsHost


I have a button inside WinformHost and I want to move it to any position inside WindowsFormsHost using mouse. I have done like below. But it is flickering when move the mouse over the button. Please help to correct.

 <Grid x:Name="myGrid" Background="Transparent" Height="400" Width="700">       
    <WindowsFormsHost Height="200" HorizontalAlignment="Left"  Margin="10,108,0,0" Name="windowsFormsHost1" VerticalAlignment="Top" Width="200" Background="PaleVioletRed">
        <wf:FlowLayoutPanel Name="FlowPanel" BackColor="Red" Dock="Fill">
            <wf:Panel.Controls> 
                <wf:Button x:Name="Btn1" MouseMove="Btn1_MouseMove" MouseDown="Btn1_MouseDown"  Width="120" Height="120"  Text="BTN1" BackColor="yellow"></wf:Button>
     <wf:Button x:Name="Btn2"  Width="120" Height="120"  Text="BTN2" BackColor="Red"></wf:Button>          
            </wf:Panel.Controls>
        </wf:FlowLayoutPanel>
    </WindowsFormsHost>
</Grid>
private System.Drawing.Point MouseDownLocation;  
private void Btn1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        btn1.Left = e.X + btn1.Left - MouseDownLocation.X;
        btn1.Top = e.Y + btn1.Top - MouseDownLocation.Y;
    }          
}

private void Btn1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        MouseDownLocation = e.Location;
    }
}

Solution

  • Reposting my comment as an answer for the karma:

    • WinForms and WPF have very different layout systems: WinForms inherits VB6's "everything is absolutely positioned" concept using only X, Y, Width and Height, which is very simple to use, but means that forms need to have a fixed-size or need to be hand-coded to handle resizing events, which is a PITA to implement.
      • WinForms did add the Anchor and Dock properties for very basic layouts.
      • In the .NET Framework 2.0, WinForms added FlowLayoutPanel and TableLayoutPanel which helped as well.
      • FlowLayoutPanel and TableLayoutPanel handle the absolute positioning of elements for you, which means you cannot directly set a control's coordinates (or rather, setting coordinates just positions the control relative to its containing panel or cell).
    • WPF, however, uses a very different layout system which does not use absolute positioning at all (you can only set coordinates inside a WPF <Canvas> element), instead it has a good assortment of built-in layout-controls such as StackPanel, DockPanel, WrapPanel, and more - which is far more flexible than WinForm's aneamic layout controls.
      • WPF and XAML were designed to support both application UIs and "documents" (remember Microsoft's ill-fated PDF competitor "XPS"? That was based on XAML's layout model) - which is why it's far more flexible than WinForms' model.
      • However, WPF has major programmer-ergonomics issues, such as being designed to only allow a single child control per Panel cell (the idea being that you should use another <Panel> as the child of the layout-control and then add children to that - but it's very counter-intuitive at first after being used to WinForm's ubiquitous ControlsCollection).
    • So my point is that WPF and WinForms have fundamentally different layout models, so don't use WPF techniques with WinForms, and vice-versa - which is why WinForms' FlowLayoutPanel doesn't work the way you think it does - if you think it works anything like WPF's WrapPanel.
    • Because WinForm's FlowLayoutPanel "takes control" over the position and size of its child controls, it means that setting Left and Top (and possibly Width and Height depending on the Dock and Anchor settings) on each child control is futile because they'll be overridden by the FlowLayoutPanel.
    • So if you want to set the positions of WinForms controls absolutely, then don't use FlowLayoutPanel at all, just add the Controls directly to the parent's ControlsCollection and then set coordinates and size.
    • The WindowsFormsHost is a WPF control, not a WinForms control, so it can only have a single WinForms control as its content (the .Child property). Use a WinForms Panel control to have a simple collection of WinForms controls that have their position and size set manually.

    Like so:

    <Grid
        x:Name="myGrid"
        Background="Transparent"
        Height="400"
        Width="700"
    >
        <WindowsFormsHost
            Height="200"
            HorizontalAlignment="Left"
            Margin="10,108,0,0"
            Name="windowsFormsHost1"
            VerticalAlignment="Top"
            Width="200"
            Background="PaleVioletRed"
        >
            <wf:Panel
                Name="FlowPanel"
                BackColor="Red"
                Dock="Fill"
            >
                <wf:Panel.Controls> 
    
                    <wf:Button
                        x:Name="Btn1"
                        MouseMove="Btn1_MouseMove"
                        MouseDown="Btn1_MouseDown"
                        Width="120"
                        Height="120"
                        Text="BTN1"
                        BackColor="yellow"
                    />
    
                    <wf:Button
                        x:Name="Btn2"
                        Width="120"
                        Height="120"
                        Text="BTN2"
                        BackColor="Red
                    />
    
                </wf:Panel.Controls>
            </wf:Panel>
        </WindowsFormsHost>
    </Grid>