성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Visual Studio 확장(VSIX)을 이용해 사용자 메뉴 추가하는 방법 (2) - 동적 메뉴 구성</h1> <p> 지난 글에서 메뉴 추가 방법에 대한 기본적인 내용을 살펴봤습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Visual Studio 확장(VSIX)을 이용해 사용자 메뉴 추가하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11184'>http://www.sysnet.pe.kr/2/0/11184</a> </pre> <br /> 그런데, 때로는 메뉴를 동적으로 구성해야 할 때가 있습니다. Visual Studio의 경우 대표적인 사례로 "File" / "Recent Files" 하위 메뉴를 들 수 있는데, 이를 위한 방법 역시 문서화가 너무 잘되어 있습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Dynamically Adding Menu Items ; <a target='tab' href='https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/extensibility/dynamically-adding-menu-items'>https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/extensibility/dynamically-adding-menu-items</a> </pre> <br /> 위의 글에 따라, 동적으로 추가되는 "매크로 명령어 목록"을 구성해 보겠습니다.<br /> <br /> 우선, 동적 메뉴로 펼쳐질 서브 메뉴의 부모가 되어줄 Menu가 있어야 하는데, 그 메뉴는 또한 Command 그룹에 속해 있어야 합니다. 기본적으로 .vsct 파일에는 ("Extensibility" / "Custom Command"를 추가한 경우) 한 개의 그룹이 등록되어 있으므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Groups> <Group guid="guidMacroCommandPackageCmdSet" id="<span style='color: blue; font-weight: bold'>MyMenuGroup</span>" priority="0x0600"> <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/> </Group> </Groups> </pre> <br /> 위의 "MyMenuGroup"과 연결될 메뉴만 하나 추가해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Menus> <Menu guid="guidMacroCommandPackageCmdSet" id="MyMenuController" priority="0x1000" type="<span style='color: blue; font-weight: bold'>MenuController</span>"> <span style='color: blue; font-weight: bold'><Parent guid="guidMacroCommandPackageCmdSet" id="MyMenuGroup" /></span> <CommandFlag>DynamicVisibility</CommandFlag> <Strings> <span style='color: blue; font-weight: bold'><ButtonText>MyMacro List</ButtonText></span> </Strings> </Menu> </Menus> </pre> <br /> 위의 상태까지만 보면, "Tools" 메뉴에 "MyMacro List"라는 이름의 메뉴가 생성되도록 정의되는데 그 메뉴의 타입이 "MenuController" 유형입니다. (이 유형이어야만 자유롭게 자신의 서브 메뉴를 정의할 수 있는 것 같습니다.)<br /> <br /> 그다음, 서브 메뉴를 담을 그룹을 하나 정의합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Groups> <Group guid="guidMacroCommandPackageCmdSet" id="MyMenuGroup" priority="0x0600"> <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/> </Group> <Group guid="guidMacroCommandPackageCmdSet" id="<span style='color: blue; font-weight: bold'>MySubMenuGroup</span>" priority="0x0600"> <span style='color: blue; font-weight: bold'><Parent guid="guidMacroCommandPackageCmdSet" id="MyMenuController" /></span> </Group> </Groups> </pre> <br /> 당연히 이 서브 메뉴 그룹의 부모는 "Tools" / "MyMacro List" 메뉴가 되어야 하므로 "<Parent />"를 통해 메뉴의 id인 "MyMenuController"를 부모로 지정해 준 것입니다. 그리고 이 서브 메뉴(예제에서는 매크로 명령어들)가 나열되기 위한 기본 버튼(이라 쓰고 메뉴라고 읽음) 하나를 지정해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Buttons> <Button guid="guidMacroCommandPackageCmdSet" id="<span style='color: blue; font-weight: bold'>cmdidMyDynamicStartCommand</span>" priority="0x0100"> <Parent guid="guidMacroCommandPackageCmdSet" id="<span style='color: blue; font-weight: bold'>MySubMenuGroup</span>" /> <CommandFlag>DynamicItemStart</CommandFlag> <CommandFlag>DynamicVisibility</CommandFlag> <CommandFlag>TextChanges</CommandFlag> <span style='color: blue; font-weight: bold'><Strings> <ButtonText>MacroSubList</ButtonText> </Strings></span> </Button> </Buttons> </pre> <br /> 마지막으로, 지금까지 설정했던 노드마다 사용되었던 ID에 대한 심벌 등록을 해줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <GuidSymbol name="guidMacroCommandPackageCmdSet" value="{6d15ea04-448b-4c65-b70c-cbfaa3c105b7}"> <IDSymbol name="MyMenuGroup" value="0x1000" /> <IDSymbol name="MySubMenuGroup" value="0x1500" /> <IDSymbol name="MyMenuController" value ="0x2000"/> <IDSymbol name="cmdidMyDynamicStartCommand" value="0x3002" /> </GuidSymbol> </pre> <br /> 일단, 여기까지의 .vsct 파일 변경과 함께 지난번의 소스 코드에서 MacroCommand의 생성자를 다음과 같이 아무 일도 안 하는 코드로 변경해서 실행하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private MacroCommand(Package package) { if (package == null) { throw new ArgumentNullException("package"); } this.package = package; } </pre> <br /> "Tools" 메뉴에 "MyMacro List" 메뉴가 포함되고, 그 하위로 "MacroSubList" 메뉴가 뜹니다.<br /> <br /> <img alt='dynamic_menu_in_vsix_1.png' src='/SysWebRes/bbs/dynamic_menu_in_vsix_1.png' /><br /> <br /> <hr style='width: 50%' /><br /> <br /> 우리가 원하는 것은, 바로 "MacroSubList"가 나와야 되는 단계의 메뉴를 동적으로 구성하는 것입니다. 이를 위해 MacroCommand의 생성자를 다음과 같이 변경해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public const int CommandId = 0x3002; // .vsct에서 지정한 cmdidMyDynamicStartCommand 심벌 값 private DTE2 dte2; private MacroCommand(Package package) { if (package == null) { throw new ArgumentNullException("package"); } _repo = new MacroRepository(); // 이 클래스 코드는 나중에 설명을 합니다. this.package = package; OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; if (commandService != null) { CommandID dynamicItemRootId = new CommandID(CommandSet, (int)CommandId); <span style='color: blue; font-weight: bold'>DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId, _repo, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);</span> commandService.AddCommand(dynamicMenuCommand); } dte2 = (DTE2)this.ServiceProvider.GetService(typeof(DTE)); } </pre> <br /> 가장 다른 점이 있다면 기존의 코드는 MenuCommand 인스턴스를 생성해 commandService.AddCommand에 건네주었던 반면, 이번에는 별도로 정의한 DynamicItemMenuCommand 타입을 생성해서 전달하고 있습니다. 이 타입은 OleMenuCommand를 상속받은 것으로 다음과 같이 코드를 추가하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.ComponentModel.Design; using System.Globalization; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using EnvDTE80; namespace VSMacro { public class <span style='color: blue; font-weight: bold'>DynamicItemMenuCommand : OleMenuCommand</span> { MacroRepository _repo; public DynamicItemMenuCommand(CommandID rootId, MacroRepository repo, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler) : base(invokeHandler, null /*changeHandler*/, beforeQueryStatusHandler, rootId) { _repo = repo; } <span style='color: blue; font-weight: bold'>public override bool DynamicItemMatch(int cmdId)</span> { if (_repo.ContainsCmd(cmdId)) { this.MatchedCommandId = cmdId; return true; } return false; } } } </pre> <br /> DynamicItemMenuCommand가 하는 것은 오로지 Visual Studio가 조회하는 Command ID가 올바른지에 대해서만 DynamicItemMatch 가상 함수를 통해 판단해 주는 것입니다.<br /> <br /> 여기서부터, 구현이 좀 복잡하고 이해가 다소 직관적이지 않은 부분이 나옵니다. 일단, Visual Studio는 (OleMenuCommand를 상속받은) 메뉴가 있는 경우, 그 메뉴의 생성자에 전달된 마지막 인자(OnBeforeQueryStatusDynamicItem)였던 delegate를 호출합니다. 즉, 다음의 코드가 실행됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args) { DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender; matchedCommand.Enabled = true; matchedCommand.Visible = true; // matchedCommand.MatchedCommandId } </pre> <br /> 그런데, 이때 실행되는 matchedCommand의 MatchedCommandId가 수상합니다. 예상으로는 생성자 호출 시에 넘겨줬던 CommandID를 따르는 CommandId(0x3002)여야 할 텐데, Visual Studio는 ("MacroSubList"로 이미 하나 점유하고 있던) 첫 번째 메뉴에 대해 matchedCommand.MatchedCommandId == 0으로 설정해서 호출합니다. 따라서, 동적 메뉴의 첫 번째 항목을 구분하는 Command ID는 0이 되고, 그에 따라 텍스트를 다음과 같은 식으로 변경해 줘야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args) { DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender; matchedCommand.Enabled = true; matchedCommand.Visible = true; if (matchedCommand.MatchedCommandId == 0) { matchedCommand.Text = "CMD1"; } } </pre> <br /> 즉, 동적 메뉴라고는 하지만 최소 1개는 이미 포함하고 있는 상태의 메뉴를 구성해야 하는 것입니다.<br /> <br /> 그다음, Visual Studio는 추가로 메뉴가 있는지에 대해 알아내야 하는데 이를 위해 (OleMenuCommand를 상속받은) DynamicItemMenuCommand로 cmdidMyDynamicStartCommand(0x3002) 값에 +1을 하면서 그 값이 유효한 지에 대해 OleMenuCommand.DynamicItemMatch 메서드를 통해 묻습니다. 따라서 표시해야 할 동적 메뉴가 2개 더 있다면 다음과 같이 구현해 줘야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public override bool DynamicItemMatch(int cmdId) { if (cmdId == 0) { return true; // 가장 첫 번째 메뉴 == 유효 } if (cmdId == (MacroCommand.CommandId + 1)) { this.MatchedCommandId = cmdId; return true; // 두 번째 메뉴 == 유효 } if (cmdId == (MacroCommand.CommandId + 2)) { this.MatchedCommandId = cmdId; return true; // 세 번째 메뉴 == 유효 } return false; // 이외의 메뉴 ID들은 유효하지 않음. } </pre> <br /> 이쯤에서, 위와 같이 구성하는 것이 다소 번거로우니 동적 메뉴를 위한 구성을 담을 MacroRepository 코드를 다음과 같이 작성합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Collections.Generic; namespace VSMacro { public class MacroRepository { List<MacroItem> _macros = new List<MacroItem>(); public MacroRepository() { _macros.Add(new MacroItem(0, "CMD1", "__cmd1__")); _macros.Add(new MacroItem(MacroCommand.CommandId + 1, "CMD2", "__cmd2__")); _macros.Add(new MacroItem(MacroCommand.CommandId + 2, "CMD3", "__cmd3__")); } public string GetCommandTextByPos(int index) { if (index != 0) { index = index - MacroCommand.CommandId; } return _macros[index].Text; } public string GetCommandSourceCodeByPos(int index) { if (index != 0) { index = index - MacroCommand.CommandId; } return _macros[index].SourceCode; } public bool ContainsCmd(int cmdId) { return _macros.Exists((item) => item.Id == cmdId); } } public class MacroItem { int _id; public int Id { get { return _id; } } string _text; public string Text { get { return _text; } } string _sourceCode; public string SourceCode { get { return _sourceCode; } } public MacroItem() { } public MacroItem(int id, string text, string sourceCode) { _id = id; _text = text; _sourceCode = sourceCode; } } } </pre> <br /> 그럼, DynamicItemMenuCommand.DynamicItemMatch 코드를 다음과 같이 변경할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public override bool DynamicItemMatch(int cmdId) { if (_repo.ContainsCmd(cmdId)) { this.MatchedCommandId = cmdId; // 이때 설정한 MatchedCommandId 값 덕분에, // 연이어 호출되는 OnBeforeQueryStatusDynamicItem 메서드에서 // 방금 전에 Visual Studio가 조회한 동적 메뉴의 ID가 무엇인지 알 수 있게 됨. return true; } return false; } </pre> <br /> MacroCommand.OnBeforeQueryStatusDynamicItem 메서드는 이렇게 정의할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args) { DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender; matchedCommand.Enabled = true; matchedCommand.Visible = true; matchedCommand.Text = _repo.GetCommandTextByPos(matchedCommand.MatchedCommandId); matchedCommand.MatchedCommandId = 0; } </pre> <br /> 위의 코드들을 보시면, 좀 이해가 안 되는 부분이 있습니다. 전통적인 생각으로는, 동적 메뉴를 구성한다면 Menu 객체가 하나 있고 그것의 Children 속성에 개별 메뉴에 해당하는 Menu 인스턴스들이 생성될 것으로 예상하게 됩니다. 가령 다음과 같은 코드를 상상할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Visual Studio 측 (가상 코드) Menu root = new Menu("MyMacro List"); MenuExtension_AddSubMenu(root); // 확장 모듈 측 MenuExtension_AddSubMenu(root) { root.Children.Add(new Menu("CMD1")); root.Children.Add(new Menu("CMD2")); root.Children.Add(new Menu("CMD3")); } </pre> <br /> 그런데, Visual Studio는 다음과 같은 식으로 동적 메뉴를 구성하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Visual Studio 측 (가상 코드) Menu root = new Menu("MyMacro List"); Menu subMenu = new Menu("MacroSubList"); OleMenuCommand dynamicMenu = new ExtensionMenu(subMenu); <span style='color: blue; font-weight: bold'>dynamicMenu.MatchedCommandId = 0;</span> dynamicMenu.OnBeforeQueryStatusDynamicItem(dynamicMenu, EventArgs.Empty); // 이때 subMenu.Text 속성에 "CMD1"을 설정해야 함. <span style='color: blue; font-weight: bold'>int startId = dynamicMenu.MatchedCommandId + 1;</span> while (true) { <span style='color: blue; font-weight: bold'>dynamicMenu.MatchedCommandId = startId;</span> if (dynamicMenu.DynamicItemMatch(startId) == false) { break; } dynamicMenu.OnBeforeQueryStatusDynamicItem(dynamicMenu, EventArgs.Empty); // 이때 2번째 이후의 동적 메뉴의 문자열을 설정 <span style='color: blue; font-weight: bold'>startId ++;</span> } </pre> <br /> 가만 보면, OleMenuCommand 인스턴스 하나로 동적으로 추가해야 할 모든 메뉴를 처리하고 있는 것입니다. 즉, OnBeforeQueryStatusDynamicItem이나 DynamicItemMatch가 호출될 때의 메뉴 인스턴스는 모두 동일하게/단일한 DynamicItemMenuCommand 인스턴스가 되는 것입니다. (그렇습니다, 처리 절차가 일반적이지 않아서 좀 혼란스럽습니다.)<br /> <br /> 암튼 저렇게 해서 결국 동적 메뉴로 구성된 항목을 사용자가 선택하면 DynamicItemMenuCommand의 3번째 인자로 넘겨준 OnInvokedDynamicItem 메서드를 호출해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void OnInvokedDynamicItem(object sender, EventArgs args) { DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender; // invokedCommand.MatchedCommandId // invokedCommand.Text } </pre> <br /> 재미있는 것은, 이 메서드가 호출될 때의 invokedCommand.MatchedCommandId는 해당 메뉴의 존재를 확인하기 위해 DynamicItemMatch를 통해 주고받았던 ID라고 장담할 수 없습니다. 왜냐하면 결국 동적 메뉴를 의미하는 DynamicItemMenuCommand 객체는 1개이기 때문에 그 인스턴스에 현재 어떤 값을 설정하고 있느냐에 따라 달라질 수 있기 때문입니다. 즉, 개발자가 작성한 코드가 어떤 식이냐에 따라 MatchedCommandId가 선택된 메뉴의 ID가 맞을 수도/맞지 않을 수도 있습니다.<br /> <br /> 사실 Visual Studio는 선택된 동적 메뉴의 MatchedCommandId를 보관하고 있지 않는 것처럼(실제로 보관하고 있지 않는 듯) 행동합니다. 그래서, 첫 번째 메뉴가 선택되었을 때 Visual Studio는 OnInvokedDynamicItem을 다음과 같은 문맥으로 호출해 줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Visual Studio 측 (가상 코드) OleMenuCommand dynamicMenu = ...; <span style='color: blue; font-weight: bold'>dynamicMenu.MatchedCommandId = 0;</span> dynamicMenu.OnBeforeQueryStatusDynamicItem(dynamicMenu, EventArgs.Empty); dynamicMenu.OnInvokedDynamicItem(dynamicMenu, EventArgs.Empty); </pre> <br /> 즉, 첫 번째 메뉴이기 때문에 MatchedCommandId를 0으로 설정하고 OnBeforeQueryStatusDynamicItem 호출로 상태를 확인한 다음 OnInvokedDynamicItem을 호출하는 것입니다.<br /> <br /> 동적으로 추가된 두 번째 메뉴부터는 말 그대로 .vsct에 등록했던 cmdidMyDynamicStartCommand(0x3002)의 값에 "+ N"을 하면서 이렇게 호출합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Visual Studio 측 (가상 코드) OleMenuCommand dynamicMenu = ...; // 2번째 메뉴가 선택되었다면, dynamicMenu.DynamicItemMatch(cmdidMyDynamicStartCommand + 1); // 호출로 인해 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 1로 설정됨 dynamicMenu.OnBeforeQueryStatusDynamicItem(dynamicMenu, EventArgs.Empty); // 호출로 인해 dynamicMenu.MatchedCommandId == 0으로 설정됨 dynamicMenu.DynamicItemMatch(cmdidMyDynamicStartCommand + 1); // 호출로 인해 다시 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 1로 설정됨 dynamicMenu.OnInvokedDynamicItem(dynamicMenu, EventArgs.Empty); // 결국 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 1 값으로 호출됨 // 3번째 메뉴가 선택되었다면, dynamicMenu.DynamicItemMatch(cmdidMyDynamicStartCommand + 2); // 호출로 인해 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 2로 설정됨 dynamicMenu.OnBeforeQueryStatusDynamicItem(dynamicMenu, EventArgs.Empty); // 호출로 인해 dynamicMenu.MatchedCommandId == 0으로 설정됨 dynamicMenu.DynamicItemMatch(cmdidMyDynamicStartCommand + 2); // 호출로 인해 다시 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 1로 설정됨 dynamicMenu.OnInvokedDynamicItem(dynamicMenu, EventArgs.Empty); // 결국 dynamicMenu.MatchedCommandId == cmdidMyDynamicStartCommand + 1 값으로 호출됨 </pre> <br /> 저 상황을 보고, DynamicItemMatch, OnBeforeQueryStatusDynamicItem의 메서드 구현을 다시 보면 왜 MatchedCommandId의 값에 0이나 cmdId를 명시적으로 설정해 주는 코드가 들어가야 하는지 이해할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 위와 같은 코드로 Visual Studio를 실행하면 다음과 같은 화면을 볼 수 있고,<br /> <br /> <img alt='dynamic_menu_in_vsix_2.png' src='/SysWebRes/bbs/dynamic_menu_in_vsix_2.png' /><br /> <br /> 각각의 메뉴를 선택하면, MacroItem 객체에 설정했던 SourceCode 속성의 문자열이 메시지 상자로 뜨는 것을 확인할 수 있습니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1133&boardid=331301885'>이 글의 첨부 파일은 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1227
(왼쪽의 숫자를 입력해야 합니다.)