VBA Windows 7个样式按钮
VBA Windows 7 style buttons
我很确定这个问题已经在网络上被问了很多,我已经在几个论坛上阅读了很多问题和他们的 "answers" 但我从来没有看到一个明确的答案所以我想知道:
有可能,使用Windows 7 种风格的按钮
在 Excel VBA 还是我必须使用这些看起来像是来自
的灰色东西
?
我 不想 使用图像,我的意思是导入这些 "ActiveX Controls",我想那是他们的名字。
我不知道可以使用 "Windows 7 style buttons" 的解决方案。然而,我想指出,编程并不要求您专门使用 "Developer" 选项卡。换句话说:仅仅因为您想要一个按钮并不意味着您必须使用“开发人员”选项卡中的按钮。事实上,几乎 Excel 内的任何形状都可以指定一个宏。
获得类似于 "Windows 7 style button" 的按钮的最简单方法是从 Shapes
菜单中 Insert
Rectangle
或 Rounded Rectangle
。当您单击该形状然后单击该形状的 Format
选项卡 select 预定义灰度 shape style
之一时,可以轻松实现 "fancy" 灰色着色。这些按钮看起来与您想要的非常相似,并且可以通过右键单击这些形状轻松 assigned a macro
。
系好安全带,你要上路了。
首先,创建一个新的 C#(或 VB.NET.. 随心所欲)class 库,并添加一个新的 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>
构建项目。
然后,添加一个新窗体,停靠一个 WPF Interop ElementHost
控件,您应该能够添加 WPF UserControl1
(无论您如何称呼它)作为托管 WPF 控件。
WPF 控件使用数据绑定来连接 Command1
和 Command2
(以及其他一切,真的 - 阅读 Model-View-ViewModel 模式),因此您需要 class 来实现 托管代码 部分。如果你的逻辑全部是 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);
}
}
}
DelegateCommand
是一个非常好的小东西,在 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);
}
}
WinForms 表单将需要分配 WPF 控件的 DataContext
- 公开一个方法来执行此操作:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void SetDataContext(ViewModel1 viewModel)
{
hostedWPFControl.DataContext = viewModel;
}
}
除此之外,这里不应该有任何代码。
WPF 喜欢 MVVM 模式,WinForms 喜欢 MVP(查找 Model-View-Presenter)。 WPF 部分托管在 WinForms 中,我们将制作一个 presenter - 这是 VBA 代码将使用的对象:
[ComVisible(true)]
public interface IPresenter1
{
void Show();
}
是的,那只是一个 接口。稍等,我们需要另一个:
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("18F3B8A8-EC60-4BCE-970A-6C0ABA145705")]
[ComVisible(true)]
public interface IPresenterEvents
{
void ExecuteCommand1(object message);
void ExecuteCommand2();
}
IPresenterEvents
接口是您的 "event sink" 接口,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();
}
}
现在,转到项目的属性,选中 "Register for COM interop" 复选框,然后构建项目;在 [Debug] 选项卡中,select start action "Start external program",然后在您的计算机上找到 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
当我从 立即 window (Ctrl+G) 运行 ThisWorkbook.DoSomething
时,我得到这个:
理论上(至少根据 MSDN),这就是您需要做的全部。正如我所说,这些事件处理程序由于某种原因没有被调用,但是嘿,你得到了闪亮的按钮! ...以及 WPF 的所有功能来设计您的 UI 现在 :)
我很确定这个问题已经在网络上被问了很多,我已经在几个论坛上阅读了很多问题和他们的 "answers" 但我从来没有看到一个明确的答案所以我想知道:
有可能,使用Windows 7 种风格的按钮
在 Excel VBA 还是我必须使用这些看起来像是来自
的灰色东西?
我 不想 使用图像,我的意思是导入这些 "ActiveX Controls",我想那是他们的名字。
我不知道可以使用 "Windows 7 style buttons" 的解决方案。然而,我想指出,编程并不要求您专门使用 "Developer" 选项卡。换句话说:仅仅因为您想要一个按钮并不意味着您必须使用“开发人员”选项卡中的按钮。事实上,几乎 Excel 内的任何形状都可以指定一个宏。
获得类似于 "Windows 7 style button" 的按钮的最简单方法是从 Shapes
菜单中 Insert
Rectangle
或 Rounded Rectangle
。当您单击该形状然后单击该形状的 Format
选项卡 select 预定义灰度 shape style
之一时,可以轻松实现 "fancy" 灰色着色。这些按钮看起来与您想要的非常相似,并且可以通过右键单击这些形状轻松 assigned a macro
。
系好安全带,你要上路了。
首先,创建一个新的 C#(或 VB.NET.. 随心所欲)class 库,并添加一个新的 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>
构建项目。
然后,添加一个新窗体,停靠一个 WPF Interop ElementHost
控件,您应该能够添加 WPF UserControl1
(无论您如何称呼它)作为托管 WPF 控件。
WPF 控件使用数据绑定来连接 Command1
和 Command2
(以及其他一切,真的 - 阅读 Model-View-ViewModel 模式),因此您需要 class 来实现 托管代码 部分。如果你的逻辑全部是 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);
}
}
}
DelegateCommand
是一个非常好的小东西,在 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);
}
}
WinForms 表单将需要分配 WPF 控件的 DataContext
- 公开一个方法来执行此操作:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void SetDataContext(ViewModel1 viewModel)
{
hostedWPFControl.DataContext = viewModel;
}
}
除此之外,这里不应该有任何代码。
WPF 喜欢 MVVM 模式,WinForms 喜欢 MVP(查找 Model-View-Presenter)。 WPF 部分托管在 WinForms 中,我们将制作一个 presenter - 这是 VBA 代码将使用的对象:
[ComVisible(true)]
public interface IPresenter1
{
void Show();
}
是的,那只是一个 接口。稍等,我们需要另一个:
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("18F3B8A8-EC60-4BCE-970A-6C0ABA145705")]
[ComVisible(true)]
public interface IPresenterEvents
{
void ExecuteCommand1(object message);
void ExecuteCommand2();
}
IPresenterEvents
接口是您的 "event sink" 接口,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();
}
}
现在,转到项目的属性,选中 "Register for COM interop" 复选框,然后构建项目;在 [Debug] 选项卡中,select start action "Start external program",然后在您的计算机上找到 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
当我从 立即 window (Ctrl+G) 运行 ThisWorkbook.DoSomething
时,我得到这个:
理论上(至少根据 MSDN),这就是您需要做的全部。正如我所说,这些事件处理程序由于某种原因没有被调用,但是嘿,你得到了闪亮的按钮! ...以及 WPF 的所有功能来设计您的 UI 现在 :)