VBA Windows 7样式按钮

Tom*_*ler 7 excel vba activex excel-vba windows-7

我很确定这个问题已在网络上被大量提问,我在几个论坛上已经阅读了很多问题及其"答案",但我从未见过明确的答案,所以我想知道:

是可以的,使用Windows 7样式按钮

在此输入图像描述

在Excel VBA中或者我必须使用这些灰色的东西看起来像它们来自

在此输入图像描述

希望使用的图片,我的意思是导入这些"ActiveX控件",我认为是他们的名字.

Ral*_*lph 6

我不知道你可以使用"Windows 7风格按钮"的解决方案.但是,我想要注意,编程不要求您只使用"开发人员"选项卡.换句话说:仅仅因为你想要一个按钮并不意味着你必须只使用Developer选项卡中的那个.实际上,Excel中几乎任何形状都可以分配一个宏.

获得类似于"Windows 7样式按钮"的按钮的最简单方法是从菜单中选择Insert一个Rectangle或一个.当您单击该形状然后在该形状的选项卡上选择一个预定义的灰度时,可以轻松实现"花式"灰色.这些按钮看起来与您想要的非常相似,通过右键单击这些形状可以轻松实现.Rounded RectangleShapesFormatshape styleassigned a macro

使用Excel创建的按钮与标准Windows 7按钮的比较


Mat*_*don 5

系好安全带,你要去兜风了。


首先,创建一个新的 C#(或 VB.NET .. 任何让你动摇的)类库,并添加一个新的 WPF UserControl,并设计你的 UI:

<UserControl x:Class="ComVisibleUI.UserControl1"
             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" 
             mc:Ignorable="d" d:DataContext="ViewModel1"
             d:DesignHeight="200" d:DesignWidth="300">

    <Grid Background="White">

        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="32" />
        </Grid.RowDefinitions>

        <TextBlock Text="actual content here" Foreground="DimGray" HorizontalAlignment="Center" VerticalAlignment="Center" />

        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="2">
            <Button Width="128" Command="{Binding Command1}">
                <TextBlock Text="Button1" />
            </Button>
            <Button Width="128" Command="{Binding Command2}">
                <TextBlock Text="Button2" />
            </Button>
        </StackPanel>

    </Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

构建项目。

然后,添加一个新的 Form,停靠一个 WPF InteropElementHost控件,您应该能够将您的 WPF UserControl1(无论您怎么称呼它)添加为托管的 WPF 控件。

WPF 控件使用数据绑定来连接Command1Command2(以及其他所有内容,实际上 - 阅读模型-视图-视图模型模式),因此您需要一个类来实现托管代码部分。如果您的逻辑全部是 VBA,那么这应该非常简单:

public class ViewModel1
{
    public ViewModel1()
    {
        _command1 = new DelegateCommand(ExecuteCommand1);
        _command2 = new DelegateCommand(ExecuteCommand2);
    }

    private readonly ICommand _command1;
    public ICommand Command1 { get { return _command1; } }

    public event EventHandler ExecutingCommand1;
    private void ExecuteCommand1(object parameter)
    {
        ExecuteHandler(ExecutingCommand1);
    }

    private readonly ICommand _command2;
    public ICommand Command2 { get { return _command2; } }

    public event EventHandler ExecutingCommand2;
    private void ExecuteCommand2(object parameter)
    {
        ExecuteHandler(ExecutingCommand2);
    }

    private void ExecuteHandler(EventHandler eventHandler)
    {
        var handler = eventHandler;
        if (handler != null)
        {
            handler.Invoke(this, EventArgs.Empty);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ADelegateCommand是一个非常好的小东西,遍布 Stack Overflow,所以如果您有任何问题,请随时搜索:

public class DelegateCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public DelegateCommand(Action<object> execute, Func<object,bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged;
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute.Invoke(parameter);
    }

    public void Execute(object parameter)
    {
        _execute.Invoke(parameter);
    }
}
Run Code Online (Sandbox Code Playgroud)

WinForms 表单将需要分配 WPF 控件的DataContext- 公开一个方法来做到这一点:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public void SetDataContext(ViewModel1 viewModel)
    {
        hostedWPFControl.DataContext = viewModel;
    }
}
Run Code Online (Sandbox Code Playgroud)

除此之外,这里不应该有任何代码


WPF 喜欢 MVVM 模式,WinForms 喜欢 MVP(查找Model-View-Presenter)。WPF 部分托管在 WinForms 中,我们将创建一个演示者- 这是 VBA 代码将使用的对象:

[ComVisible(true)]
public interface IPresenter1
{
    void Show();
}
Run Code Online (Sandbox Code Playgroud)

是的,那只是一个界面。等等,我们还需要一个:

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("18F3B8A8-EC60-4BCE-970A-6C0ABA145705")]
[ComVisible(true)]
public interface IPresenterEvents
{
    void ExecuteCommand1(object message);
    void ExecuteCommand2();
}
Run Code Online (Sandbox Code Playgroud)

IPresenterEvents接口是您的“事件接收器”接口,VBA 代码需要实现该接口,但我会谈到它。首先我们需要实现实际的演示者:

public delegate void Command1Delegate(string message);
public delegate void Command2Delegate();

[ComSourceInterfaces(typeof(IPresenterEvents))]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("FAF36F86-7CB3-4E0C-A016-D8C84F6B07D7")]
public class Presenter1 : IPresenter1, IDisposable
{
    private readonly Form _view;

    public Presenter1()
    {
        var view = new Form1();
        var viewModel = new ViewModel1();
        viewModel.ExecutingCommand1 += viewModel_ExecutingCommand1;
        viewModel.ExecutingCommand2 += viewModel_ExecutingCommand2;

        view.SetDataContext(viewModel);

        _view = view;
    }

    public event Command1Delegate ExecuteCommand1;
    private void viewModel_ExecutingCommand1(object sender, EventArgs e)
    {
        var handler = ExecuteCommand1;
        if (handler != null)
        {
            handler.Invoke("Hello from Command1!");
        }
    }

    public event Command2Delegate ExecuteCommand2;
    private void viewModel_ExecutingCommand2(object sender, EventArgs e)
    {
        var handler = ExecuteCommand2;
        if (handler != null)
        {
            handler.Invoke();
        }
    }

    public void Show()
    {
        _view.ShowDialog();
    }

    public void Dispose()
    {
        _view.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,转到项目的属性,并选中“注册 COM 互操作”复选框,然后构建项目;在[调试]选项卡中,选择启动操作“启动外部程序”,然后在您的机器上找到EXCEL.EXE可执行文件:当您按F5时,Visual Studio将启动带有调试器的Excel,然后您可以打开VBE (Alt+F11),添加对您刚刚构建的 .tlb(类型库)的引用(您将在 .net 项目目录中的 下找到它\bin\debug\theprojectname.tlb,假设是调试版本),然后就可以了。

这里有很多问题,我稍后会回来修复:

  • Dispose()方法没有公开,也不会在任何时候显式或隐式调用,这是......脏。
  • 虽然从 C# 调试器的角度来看一切似乎都在工作,但我无法运行该死的 VBA 处理程序。如果您打算在 VBA 中实现逻辑,而不仅仅是调出 UI,这可能是一个大问题。OTOH 你可以访问 .net 代码,也可以在演示器本身中实现演示器逻辑,在 C#/VB.NET 中,然后你不需要让这些事件处理程序工作。

无论如何,我已将此代码添加到ThisWorkbook

Option Explicit
Private WithEvents view As ComVisibleUI.Presenter1

Public Sub DoSomething()
    Set view = New ComVisibleUI.Presenter1
    view.Show
End Sub

Private Sub view_ExecuteCommand1(ByVal message As Variant)
    MsgBox message
End Sub

Private Sub view_ExecuteCommand2()
    MsgBox "Hello from WPF!"
End Sub
Run Code Online (Sandbox Code Playgroud)

当我ThisWorkbook.DoSomething直接窗口(Ctrl + G)运行时,我得到了这个:

带有闪亮命令按钮的 WPF 驱动的 UI

理论上(至少根据MSDN),这就是您需要做的全部。正如我所说,这些事件处理程序由于某种原因没有被调用,但是,嘿,你得到了闪亮的按钮!...以及 WPF 的所有功能来设计您的 UI :)

  • 神圣的 oO 感谢您的努力,我会在几天内尝试一下,如果有效,请告诉您 (2认同)