2015-09-01 21 views
6

Tôi chắc chắn câu hỏi này đã được hỏi rất nhiều trên web và tôi đã đọc rất nhiều câu hỏi và "câu trả lời" của họ trên một số diễn đàn nhưng tôi chưa bao giờ thấy câu trả lời rõ ràng vì vậy tôi muốn biết:VBA Các nút kiểu Windows 7

is là có thể, sử dụng Windows 7 nút phong cách

enter image description here

trong Excel VBA hay tôi phải sử dụng những thứ màu xám trông như họ đến từ

enter image description here

?

I không muốn sử dụng hình ảnh, ý tôi là nhập "Điều khiển ActiveX" này, tôi nghĩ đó là tên của chúng.

+0

tôi rất muốn biết điều đó. – DeerSpotter

+6

* Tôi có phải sử dụng những thứ màu xám trông giống như chúng đến từ Windows 2000 * - tôi nghĩ chúng thực sự từ Win95. Nếu bạn muốn * sáng bóng và mới *, bạn có thể làm cho giao diện người dùng của bạn với WPF/XAML trong một thư viện lớp .net có thể nhìn thấy COM (.dll) và sử dụng * đó * từ mã VBA của bạn. Tùy thuộc vào kiểu mã hóa của bạn, * có thể * có nghĩa là những thay đổi kiến ​​trúc nghiêm trọng (ví dụ nếu bạn quen với việc thực hiện tất cả các logic trong các nút bấm '' Nhấp chuột ...) –

+6

Dịch: Nếu bạn biết đủ lập trình để có thể làm nó, bạn biết rằng bạn không muốn. – Kaz

Trả lời

3

thắt dây an toàn, bạn đang ở cho một chuyến đi.


Đầu tiên, tạo một C# mới (hoặc VB.NET .. bất cứ điều gì đá thuyền của bạn) thư viện lớp, và thêm một WPF UserControl mới, và thiết kế giao diện người dùng của bạn:

<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> 

Xây dựng dự án.

Sau đó, thêm Biểu mẫu mới, gắn điều khiển WPF Interop ElementHost và bạn có thể thêm WPF UserControl1 (bất kể bạn gọi nó là gì) làm điều khiển WPF được lưu trữ.

Việc kiểm soát WPF sử dụng các ràng buộc dữ liệu để treo lên Command1Command2 (và mọi thứ khác, thực sự - đọc lên trên mẫu Model-View-ViewModel), vì vậy bạn sẽ cần một lớp học để thực hiện mã số quản lý một phần.Nếu logic của bạn là tất cả VBA thì đây nên được khá mỏng:

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); 
     } 
    } 
} 

Một DelegateCommand là một điều rất tốt đẹp chút đó là khắp nơi trên Stack Overflow, vì vậy đừng ngần ngại tìm kiếm nếu bạn có bất cứ thắc mắc:

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); 
    } 
} 

dạng WinForms sẽ cần phải gán sự kiểm soát WPF của DataContext - phơi bày một phương pháp để làm điều đó:

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

    public void SetDataContext(ViewModel1 viewModel) 
    { 
     hostedWPFControl.DataContext = viewModel; 
    } 
} 

Ngoài ra, không được có bất kỳ mã nào ở đây.


WPF thích mô hình MVVM, WinForms thích MVP (tra cứu Model-View-Presenter). Phần WPF đang được lưu trữ trong WinForms, chúng tôi sẽ làm cho một người dẫn chương trình - đó là đối tượng mã VBA sẽ sử dụng:

[ComVisible(true)] 
public interface IPresenter1 
{ 
    void Show(); 
} 

Vâng, đó chỉ là một giao diện . Giữ trên, chúng ta cần một:

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

Giao diện IPresenterEvents là của bạn "chìm sự kiện" giao diện, rằng mã VBA sẽ cần phải thực hiện, nhưng tôi sẽ nhận được nó. Trước tiên, chúng tôi cần phải trình bày người trình bày thực tế:

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(); 
    } 
} 

Bây giờ, hãy vào thuộc tính của dự án và chọn hộp kiểm "Đăng ký COM interop", sau đó xây dựng dự án; trong tab [Debug], chọn bắt đầu hành động "Bắt đầu chương trình bên ngoài" và xác định tệp EXCEL.EXE trên máy của bạn: khi bạn nhấn F5, Visual Studio sẽ khởi chạy Excel với trình gỡ lỗi được đính kèm và sau đó bạn có thể mở VBE (Alt + F11), thêm một tham chiếu đến .tlb (loại thư viện) mà bạn vừa xây dựng (bạn sẽ tìm thấy nó trong thư mục dự án .net của bạn, dưới \bin\debug\theprojectname.tlb, giả sử một bản dựng gỡ lỗi), và nên làm điều đó .

Có một số vấn đề ở đây, rằng tôi sẽ trở lại để sửa chữa sau:

  • Phương pháp Dispose() không được tiếp xúc, và sẽ không được rõ ràng hoặc ngầm gọi bất cứ lúc nào, mà là ... bẩn.
  • Trong khi mọi thứ có vẻ như nó đang làm việc từ quan điểm của trình gỡ lỗi C#, tôi không thể có được trình xử lý VBA darn để chạy. Đó có lẽ là một vấn đề lớn nếu bạn có ý định thực hiện logic trong VBA, không chỉ đưa lên giao diện người dùng. OTOH bạn có quyền truy cập vào mã .net, cũng có thể triển khai logic trình bày trong chính trình bày, trong C#/VB.NET, và sau đó bạn không cần phải xử lý sự kiện này để làm việc.

Dù sao, tôi đã thêm mã này vào 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 

Và khi tôi chạy ThisWorkbook.DoSomething từ cửa sổ ngay lập tức (Ctrl + G), tôi có được điều này:

WPF-powered UI with shiny command buttons

Theo lý thuyết (ít nhất theo MSDN), đó là tất cả những gì bạn cần làm. Như tôi đã nói các trình xử lý sự kiện này không được gọi vì một lý do nào đó, nhưng hey, bạn có các nút sáng bóng của mình! ... và tất cả sức mạnh của WPF để thiết kế giao diện người dùng của bạn ngay bây giờ :)

+1

Holy o.O Cảm ơn những nỗ lực này, tôi sẽ cố gắng trong vài ngày và cho bạn biết nếu nó hoạt động –

5

Tôi không biết giải pháp nào bạn có thể sử dụng "nút kiểu Windows 7". Tuy nhiên, tôi muốn lưu ý rằng lập trình không yêu cầu bạn chỉ sử dụng tab "Nhà phát triển". Nói cách khác: chỉ vì bạn muốn một nút không có nghĩa là bạn phải chỉ sử dụng nút đó từ tab Nhà phát triển. Trong thực tế, hầu như bất kỳ hình dạng nào trong Excel đều có thể được gán một macro.

Cách dễ nhất để nhận nút tương tự như "nút kiểu Windows 7" là Insert a Rectangle hoặc Rounded Rectangle từ menu Shapes. Màu xám "ưa thích" có thể dễ dàng đạt được khi bạn nhấp vào hình dạng đó và sau đó trên tab Format cho hình dạng đó chọn một trong các màu xám được xác định trước shape style. Các nút này trông rất giống với những gì bạn muốn và có thể dễ dàng assigned a macro bằng cách nhấp chuột phải vào các hình dạng này.

A comparison of buttons created with Excel shapes to the standard Windows 7 button

+0

++ Một nút trên trang tính thực sự được lưu trữ trong bộ sưu tập 'Hình dạng' của trang tính; giải pháp này là đơn giản nhất, và bạn có thể nhận được kết quả khá gọn gàng với các hiệu ứng và đổ bóng. –

+0

Ý của bạn là gì với "trình đơn hình dạng"? –

+0

'Insert' ->' Illustrations' -> 'Shapes' ->' Rounded Rectangle' hoặc 'Rectangle' – Ralph