Embed Windows form panel in WPF control

733 views Asked by At

I'm trying to design fancy WPF controls for Windows desktop application. I'm wondering if it is possible to expose an embedded panel in a WPF control. The idea is to create a panel with a entitled ribbon. A button will be used to deploy or hide contained controls.

Here is my new panel :

<UserControl x:Class="TestRibbonPanel.RibbonPanel.XamlRibbonPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" 
xmlns:local="clr-namespace:TestRibbonPanel.RibbonPanel"
mc:Ignorable="d" 
d:DesignHeight="150" d:DesignWidth="200" MinHeight="40" x:Name="uc">
<UserControl.Resources>
    <local:BorderHeightConverter x:Key="BhConverter"/>
</UserControl.Resources>
<Grid Background="Transparent">
    <Grid x:Name="grd" Height="20" VerticalAlignment="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <Button Grid.Column="0" Grid.Row="0" x:Name="btn" Background="#333333" Click="Btn_Click"/>
        <TextBlock Grid.Column="1" Grid.Row="0" Text="Title" Background="#333333" Foreground="#CCCCCC"/>
    </Grid>
    <Popup x:Name="pu" StaysOpen="True" PlacementTarget="{Binding ElementName=grd}" AllowsTransparency="True" PopupAnimation="Slide" IsOpen="True">
        <Border x:Name="brd" Background="White" CornerRadius="0,0,10,10" BorderThickness="2" BorderBrush="Black" Height="{Binding Path=ActualHeight, ElementName=uc, Converter={StaticResource BhConverter}, FallbackValue=130}" Width="{Binding ActualWidth, ElementName=grd, FallbackValue=200}" SizeChanged="Brd_SizeChanged">
            <WindowsFormsHost>
                <wf:Panel x:Name="pnl" BorderStyle="FixedSingle" BackColor="Red"/>
            </WindowsFormsHost>
        </Border>
    </Popup>
</Grid>

In the code behind, i have used a System.Windows.Controls.Panel as a property named ContentPanel :

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace TestRibbonPanel.RibbonPanel
{
/// <summary>
/// Logique d'interaction pour XamlRibbonPanel.xaml
/// </summary>
public partial class XamlRibbonPanel : UserControl
{
    public static readonly DependencyProperty IsOPenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(XamlRibbonPanel), new PropertyMetadata(true));
    public bool IsOPen
    {
        get { return (bool)GetValue(IsOPenProperty); }
        set
        {
            SetValue(IsOPenProperty, value);
        }
    }
    public System.Windows.Forms.Panel ContentPanel
    {
        get { return pnl; }
    }

    public XamlRibbonPanel()
    {
        InitializeComponent();
    }
    private void Brd_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        pnl.Size = new System.Drawing.Size((int)brd.ActualWidth, (int)brd.ActualHeight);
    }

    private void Btn_Click(object sender, RoutedEventArgs e)
    {
        pu.IsOpen = !pu.IsOpen;
    }
}

public class BorderHeightConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (double)value - 20;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    }
}

Now i put this WPF control in a windows form wrapper using ElementHost integration class named EH embedding the XamlRibbonPanel control.:

    [DesignerAttribute(typeof(XRibbonPanelDesigner))]
public class XRibbonPanel : UserControl
{
    private System.ComponentModel.IContainer _components = null;
    private System.Windows.Forms.Integration.ElementHost EH;
    private RibbonPanel.XamlRibbonPanel _xp = null;
    private Panel _contentPanel = null;

    public Panel ContentPanel
    {
        get { return _contentPanel; }
    }

    /// <summary>
    /// .ctor
    /// </summary>
    public XRibbonPanel()
    {
        InitializeComponent();
        _contentPanel = xp.ContentPanel;
    }

    private void InitializeComponent()
    {
        this.EH = new System.Windows.Forms.Integration.ElementHost();
        this._xp = new TestRibbonPanel.RibbonPanel.XamlRibbonPanel();
        this.SuspendLayout();
        // 
        // EH
        // 
        this.EH.Dock = System.Windows.Forms.DockStyle.Fill;
        this.EH.Location = new System.Drawing.Point(0, 0);
        this.EH.Name = "EH";
        this.EH.Size = new System.Drawing.Size(150, 150);
        this.EH.TabIndex = 0;
        this.EH.Text = "";
        this.EH.Child = _xp;
        // 
        // XRibbonPanel
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.Controls.Add(this.EH);
        this.Name = "XRibbonPanel";
        this.ResumeLayout(false);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && (_components != null))
        {
            _components.Dispose();
        }
        EH.Dispose();
        base.Dispose(disposing);
    }
}

As you can see, i have used a custom designer for the control in the hope of exposing the control to design. It's a technique i have read here and successfully applied for other 'classic' controls :

    using System;
using System.ComponentModel;
using System.Windows.Forms.Design;

namespace TestRibbonPanel
{
    public class XRibbonPanelDesigner : ScrollableControlDesigner
    {
        XRibbonPanel _panel;
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);
            _panel = (XRibbonPanel)component;

            if (_panel == null)
                throw new ArgumentException();
            else
            {
                System.Windows.Forms.Panel pnl = _panel.ContentPanel;
                if (pnl != null)
                {
                    bool result = EnableDesignMode(pnl, "Layout");
                }
            }
        }
    }
}

When i put a XRibbonPanel on a form, the panel act as desired (opening and closing panel). But unfortunally, i can't see the 'Layout' object that i usually use to drop off nested controls. Another annoying effect, when i move the form, the popup Xaml Control float on the desktop dissociated from the original form. It also happen in design mode when i leave the form tab open for instance.

If i set a breakpoint on :

bool result = EnableDesignMode(pnl, "Layout"); 

in the designer, pnl is not null and the boolean result is true.

Any clue ?

0

There are 0 answers