feat(legacy): add shell-independent module page compatibility
This commit is contained in:
@@ -260,6 +260,16 @@ namespace Lskj.Main.Control
|
||||
{
|
||||
try
|
||||
{
|
||||
// Bind the real WinForms page surface before any menu or
|
||||
// startup callback can open a module. The WPF host never
|
||||
// constructs this panel; the explicit shell check keeps its
|
||||
// ModulePageHost untouched if a shared AppDomain is used.
|
||||
if (Manager.ExternalMainShell == null)
|
||||
{
|
||||
ERPInfo.Instance.ModulePageHost = null;
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
Manager.TabMain = this.tabMain;
|
||||
}
|
||||
WaitForm.ShowForm();
|
||||
|
||||
ContextMenuStrip headMenuStrip = new ContextMenuStrip();
|
||||
@@ -366,7 +376,6 @@ namespace Lskj.Main.Control
|
||||
new Thread(MainImpl.DnsHostEntry).Start();
|
||||
this.mark_timer.Start();
|
||||
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
ERPInfo.Instance.ModuleForms = Manager.ModuleForms;
|
||||
if ((!this._allMenus.Columns.Contains("menuposition") || (!SystemInfo.Instance.IsOpenWork && !SystemInfo.Instance.ShowGallerysFromMenu)) && (!SystemInfo.Instance.HideGallerys || SystemInfo.Instance.DesktopFormatShowGallerys))
|
||||
{
|
||||
@@ -3850,12 +3859,12 @@ namespace Lskj.Main.Control
|
||||
XtraTabPage beforePage = m.BeforePage;
|
||||
if (beforePage != null)
|
||||
{
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.PageControl.TabPages)
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.ModulePages)
|
||||
{
|
||||
|
||||
if (tablepage == beforePage)
|
||||
{
|
||||
ERPInfo.Instance.PageControl.SelectedTabPage = beforePage;
|
||||
ERPInfo.Instance.CurrentModulePage = beforePage;
|
||||
if (isclose) tabPage.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -4945,6 +4954,7 @@ namespace Lskj.Main.Control
|
||||
try
|
||||
{
|
||||
XtraTabPage tabPage = this.tabMain.SelectedTabPage;
|
||||
ERPInfo.Instance.NotifyModulePageSelected(tabPage);
|
||||
string guid = tabPage.Tag + "";
|
||||
if (!string.IsNullOrWhiteSpace(guid) && Manager.ModuleForms.ContainsKey(guid))
|
||||
{
|
||||
|
||||
@@ -61,6 +61,17 @@ namespace Lskj.Main.Hosting
|
||||
Manager.ExternalMainShell = externalMainShell;
|
||||
try
|
||||
{
|
||||
// A standalone Ls_ERP process must always start in the
|
||||
// original WinForms page mode. Clear any stale host
|
||||
// references before FrmMain is created so Manager.OpenModule
|
||||
// cannot accidentally route pages to a previous WPF shell
|
||||
// when the legacy runtime is reused in-process.
|
||||
if (externalMainShell == null)
|
||||
{
|
||||
ERPInfo.Instance.ModulePageHost = null;
|
||||
ERPInfo.Instance.PageControl = null;
|
||||
Manager.TabMain = null;
|
||||
}
|
||||
Program.InitializeMainProcess();
|
||||
_mainProcessInitialized = true;
|
||||
}
|
||||
|
||||
@@ -356,12 +356,12 @@ namespace Lskj.Main.Model
|
||||
dllName = MainImpl.GetDefaultValue(dllName);
|
||||
if (SystemInfo.Instance.SoftOpenMode || SingleOpenMode)
|
||||
{
|
||||
foreach (XtraTabPage tablepage in TabMain.TabPages)
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.ModulePages)
|
||||
{
|
||||
if (tablepage.Text == menuName)
|
||||
{
|
||||
MessageUtil.Show("已设置不能打开多个相同模块!");
|
||||
TabMain.SelectedTabPage = tablepage;
|
||||
ERPInfo.Instance.SelectModulePage(tablepage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -414,8 +414,8 @@ namespace Lskj.Main.Model
|
||||
tp.Dock = DockStyle.Fill;
|
||||
module.Tag = guid;
|
||||
module.ModuleInPtr = exeMainHandle;
|
||||
TabMain.TabPages.Add(tp);
|
||||
TabMain.SelectedTabPage = tp;
|
||||
ERPInfo.Instance.AddModulePage(tp);
|
||||
ERPInfo.Instance.SelectModulePage(tp);
|
||||
ModuleForms.Add(guid, module);
|
||||
}
|
||||
else//直接打开exe模块
|
||||
@@ -688,8 +688,8 @@ namespace Lskj.Main.Model
|
||||
//}
|
||||
}
|
||||
|
||||
TabMain.TabPages.Add(tp);
|
||||
TabMain.SelectedTabPage = tp;
|
||||
ERPInfo.Instance.AddModulePage(tp);
|
||||
ERPInfo.Instance.SelectModulePage(tp);
|
||||
ModuleForms.Add(guid, module);
|
||||
|
||||
//LastModuleForm.Add(module);
|
||||
@@ -720,10 +720,7 @@ namespace Lskj.Main.Model
|
||||
if (tabPage != null)
|
||||
{
|
||||
StaticControl.DogVerifyModuleForms.Remove(tabPage);
|
||||
if (TabMain != null && TabMain.TabPages.Contains(tabPage))
|
||||
{
|
||||
TabMain.TabPages.Remove(tabPage);
|
||||
}
|
||||
ERPInfo.Instance.RemoveModulePage(tabPage);
|
||||
}
|
||||
|
||||
if (form != null && !form.IsDisposed)
|
||||
|
||||
@@ -194,7 +194,8 @@ namespace Lskj.Main3.Control
|
||||
// 解析dns
|
||||
new Thread(MainImpl.DnsHostEntry).Start();
|
||||
this.mark_timer.Start();
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
if (ERPInfo.Instance.ModulePageHost == null)
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
ERPInfo.Instance.ModuleForms = Manager.ModuleForms;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1976,12 +1977,12 @@ namespace Lskj.Main3.Control
|
||||
XtraTabPage beforePage = m.BeforePage;
|
||||
if (beforePage != null)
|
||||
{
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.PageControl.TabPages)
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.ModulePages)
|
||||
{
|
||||
|
||||
if (tablepage == beforePage)
|
||||
{
|
||||
ERPInfo.Instance.PageControl.SelectedTabPage = beforePage;
|
||||
ERPInfo.Instance.CurrentModulePage = beforePage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2584,6 +2585,7 @@ namespace Lskj.Main3.Control
|
||||
if (!SystemInfo.Instance.RefreshNotify)
|
||||
{
|
||||
XtraTabPage tabPage = this.tabMain.SelectedTabPage;
|
||||
ERPInfo.Instance.NotifyModulePageSelected(tabPage);
|
||||
if (tabPage.TabIndex == 0)
|
||||
{
|
||||
Thread thread = new Thread(SetNotifys);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/***********************
|
||||
/***********************
|
||||
* 说明:主窗体界面的构建
|
||||
* 创建人:龚宇超
|
||||
* 创建日期:
|
||||
@@ -238,7 +238,8 @@ namespace Lskj.Main4.Control
|
||||
// 解析dns
|
||||
new Thread(MainImpl.DnsHostEntry).Start();
|
||||
this.mark_timer.Start();
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
if (ERPInfo.Instance.ModulePageHost == null)
|
||||
ERPInfo.Instance.PageControl = this.tabMain;
|
||||
ERPInfo.Instance.ModuleForms = Manager.ModuleForms;
|
||||
|
||||
splitMainControl.SplitterMoved += new SplitterEventHandler(this.OnSplitterPositionChanged);
|
||||
@@ -2099,12 +2100,12 @@ namespace Lskj.Main4.Control
|
||||
XtraTabPage beforePage = m.BeforePage;
|
||||
if (beforePage != null)
|
||||
{
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.PageControl.TabPages)
|
||||
foreach (XtraTabPage tablepage in ERPInfo.Instance.ModulePages)
|
||||
{
|
||||
|
||||
if (tablepage == beforePage)
|
||||
{
|
||||
ERPInfo.Instance.PageControl.SelectedTabPage = beforePage;
|
||||
ERPInfo.Instance.CurrentModulePage = beforePage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2872,6 +2873,7 @@ namespace Lskj.Main4.Control
|
||||
if (!SystemInfo.Instance.RefreshNotify)
|
||||
{
|
||||
XtraTabPage tabPage = this.tabMain.SelectedTabPage;
|
||||
ERPInfo.Instance.NotifyModulePageSelected(tabPage);
|
||||
if (tabPage.TabIndex == 0)
|
||||
{
|
||||
Thread thread = new Thread(SetNotifys);
|
||||
|
||||
@@ -88,6 +88,311 @@ namespace Lskj.Model
|
||||
/// 主界面page界面
|
||||
/// </summary>
|
||||
public XtraTabControl PageControl;
|
||||
|
||||
private readonly object _modulePageCallbackSync = new object();
|
||||
private readonly List<Action<string>> _modulePageSelectedCallbacks =
|
||||
new List<Action<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// 顶层页面生命周期服务。旧 WinForms 主程序保持为空并继续使用
|
||||
/// PageControl;WPF 主程序注入自己的页面宿主实现。
|
||||
/// </summary>
|
||||
public IModulePageHost ModulePageHost;
|
||||
|
||||
public XtraTabPage CurrentModulePage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ModulePageHost != null)
|
||||
return ModulePageHost.SelectedPage;
|
||||
return PageControl == null ? null : PageControl.SelectedTabPage;
|
||||
}
|
||||
set
|
||||
{
|
||||
// Keep the original assignment-based API working for legacy
|
||||
// modules. In the classic WinForms shell this forwards to
|
||||
// the real PageControl; when WPF injects ModulePageHost it
|
||||
// selects the page through that host instead of creating a
|
||||
// second XtraTabControl.
|
||||
SelectModulePage(value);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<XtraTabPage> ModulePages
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ModulePageHost != null)
|
||||
return ModulePageHost.Pages;
|
||||
if (PageControl == null)
|
||||
return new XtraTabPage[0];
|
||||
return PageControl.TabPages.Cast<XtraTabPage>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a top-level module page surface has been
|
||||
/// initialized. The classic shell exposes its XtraTabControl while
|
||||
/// WPF exposes IModulePageHost; callers can use this without reaching
|
||||
/// into either visual implementation.
|
||||
/// </summary>
|
||||
public bool HasModulePageSurface
|
||||
{
|
||||
get { return ModulePageHost != null || PageControl != null; }
|
||||
}
|
||||
|
||||
public void AddModulePage(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return;
|
||||
if (ModulePageHost != null)
|
||||
{
|
||||
ModulePageHost.AddPage(page);
|
||||
return;
|
||||
}
|
||||
if (PageControl != null && !PageControl.TabPages.Contains(page))
|
||||
PageControl.TabPages.Add(page);
|
||||
}
|
||||
|
||||
public bool RemoveModulePage(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return false;
|
||||
if (ModulePageHost != null)
|
||||
return ModulePageHost.RemovePage(page);
|
||||
if (PageControl == null || !PageControl.TabPages.Contains(page))
|
||||
return false;
|
||||
PageControl.TabPages.Remove(page);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SelectModulePage(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return false;
|
||||
if (ModulePageHost != null)
|
||||
return ModulePageHost.SelectPage(page);
|
||||
if (PageControl == null || !PageControl.TabPages.Contains(page))
|
||||
return false;
|
||||
PageControl.SelectedTabPage = page;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ContainsModulePage(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return false;
|
||||
if (ModulePageHost != null)
|
||||
return ModulePageHost.ContainsPage(page);
|
||||
return PageControl != null && PageControl.TabPages.Contains(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the stable logical identifier used to bind a legacy page to
|
||||
/// its WPF tab. The existing Tag value is retained whenever the old
|
||||
/// module already assigned one; a generated value is written only for
|
||||
/// an otherwise unidentified page.
|
||||
/// </summary>
|
||||
public string GetModulePageId(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return string.Empty;
|
||||
|
||||
string pageId = Convert.ToString(page.Tag);
|
||||
if (string.IsNullOrWhiteSpace(pageId))
|
||||
{
|
||||
pageId = "legacy-module-" + Guid.NewGuid().ToString("N");
|
||||
page.Tag = pageId;
|
||||
}
|
||||
return pageId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logical identifier of the selected top-level page without
|
||||
/// exposing the shell's visual tab-control implementation.
|
||||
/// </summary>
|
||||
public string CurrentModulePageId
|
||||
{
|
||||
get { return GetModulePageId(CurrentModulePage); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates top-level page identifiers for modules that only need to
|
||||
/// compare or select pages and should not reach PageControl.TabPages.
|
||||
/// </summary>
|
||||
public IEnumerable<string> GetModulePageIds()
|
||||
{
|
||||
return ModulePages.Select(GetModulePageId).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects a top-level page by its logical identifier. Both the WPF
|
||||
/// host and the classic WinForms PageControl use the same fallback
|
||||
/// path as the original XtraTabPage-based API.
|
||||
/// </summary>
|
||||
public bool SelectModulePage(string pageId)
|
||||
{
|
||||
XtraTabPage page = FindModulePage(pageId);
|
||||
return page != null && SelectModulePage(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a top-level page by its logical identifier.
|
||||
/// </summary>
|
||||
public bool RemoveModulePage(string pageId)
|
||||
{
|
||||
XtraTabPage page = FindModulePage(pageId);
|
||||
return page != null && RemoveModulePage(page);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the supplied logical identifier belongs to a
|
||||
/// top-level module page. This replaces checks based on
|
||||
/// page.Parent as XtraTabControl in migrated module code.
|
||||
/// </summary>
|
||||
public bool IsTopLevelModulePage(string pageId)
|
||||
{
|
||||
return FindModulePage(pageId) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a logical page-selection callback. This is intentionally
|
||||
/// a page-service callback rather than a subscription to a concrete
|
||||
/// WinForms or WPF tab-control event. Dispose the returned token when
|
||||
/// the module closes so the callback cannot retain the module.
|
||||
/// </summary>
|
||||
public IDisposable RegisterModulePageSelected(Action<string> callback)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
lock (_modulePageCallbackSync)
|
||||
{
|
||||
_modulePageSelectedCallbacks.Add(callback);
|
||||
}
|
||||
return new ModulePageCallbackRegistration(this, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback for one logical module page. This overload is
|
||||
/// the source-compatible replacement for code that used to subscribe
|
||||
/// to the top-level PageControl.SelectedPageChanged event and then
|
||||
/// filter by the selected page. The callback receives no WinForms
|
||||
/// event arguments, so the same module code works when the WPF shell
|
||||
/// owns the visible tab while the direct WinForms shell still uses its
|
||||
/// original XtraTabControl.
|
||||
/// </summary>
|
||||
public IDisposable RegisterModulePageSelected(
|
||||
string modulePageId,
|
||||
Action callback)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
string expectedPageId = (modulePageId ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(expectedPageId))
|
||||
throw new ArgumentException(
|
||||
"模块页面标识不能为空。",
|
||||
"modulePageId");
|
||||
|
||||
return RegisterModulePageSelected(
|
||||
selectedPageId =>
|
||||
{
|
||||
if (string.Equals(
|
||||
selectedPageId,
|
||||
expectedPageId,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies logical page-selection subscribers. The WPF page host
|
||||
/// calls this after its own selection state is updated; the classic
|
||||
/// MainPanelControlEx calls it from its existing SelectedPageChanged
|
||||
/// handler. No visual control event is introduced in the WPF path.
|
||||
/// </summary>
|
||||
public void NotifyModulePageSelected(XtraTabPage page)
|
||||
{
|
||||
if (page == null)
|
||||
return;
|
||||
|
||||
string pageId = GetModulePageId(page);
|
||||
Action<string>[] callbacks;
|
||||
lock (_modulePageCallbackSync)
|
||||
{
|
||||
callbacks = _modulePageSelectedCallbacks.ToArray();
|
||||
}
|
||||
|
||||
foreach (Action<string> callback in callbacks)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(pageId);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine(
|
||||
"旧模块页面回调执行失败:" + exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private XtraTabPage FindModulePage(string pageId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pageId))
|
||||
return null;
|
||||
|
||||
foreach (XtraTabPage page in ModulePages)
|
||||
{
|
||||
if (string.Equals(
|
||||
GetModulePageId(page),
|
||||
pageId,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
return page;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void UnregisterModulePageSelected(Action<string> callback)
|
||||
{
|
||||
if (callback == null)
|
||||
return;
|
||||
|
||||
lock (_modulePageCallbackSync)
|
||||
{
|
||||
_modulePageSelectedCallbacks.Remove(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ModulePageCallbackRegistration : IDisposable
|
||||
{
|
||||
private ERPInfo _owner;
|
||||
private readonly Action<string> _callback;
|
||||
|
||||
public ModulePageCallbackRegistration(
|
||||
ERPInfo owner,
|
||||
Action<string> callback)
|
||||
{
|
||||
_owner = owner;
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ERPInfo owner = _owner;
|
||||
if (owner == null)
|
||||
return;
|
||||
|
||||
_owner = null;
|
||||
owner.UnregisterModulePageSelected(_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主界面page界面字典
|
||||
/// </summary>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ModulePageHost.cs" />
|
||||
<Reference Include="DevExpress.Utils.v15.2, Version=15.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\Debug\DevExpress.Utils.v15.2.dll</HintPath>
|
||||
@@ -111,4 +112,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DevExpress.XtraTab;
|
||||
|
||||
namespace Lskj.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the top-level module page lifecycle without coupling module
|
||||
/// code to a particular visual tab control. The WPF shell supplies an
|
||||
/// implementation backed by DXTabItem instances; the classic WinForms
|
||||
/// shell leaves this interface unset and ERPInfo falls back to its real
|
||||
/// XtraTabControl/PageControl.
|
||||
/// </summary>
|
||||
public interface IModulePageHost
|
||||
{
|
||||
event EventHandler<ModulePageEventArgs> PageAdded;
|
||||
event EventHandler<ModulePageEventArgs> PageRemoved;
|
||||
event EventHandler<ModulePageEventArgs> PageSelected;
|
||||
|
||||
XtraTabPage SelectedPage { get; }
|
||||
IEnumerable<XtraTabPage> Pages { get; }
|
||||
|
||||
void AddPage(XtraTabPage page);
|
||||
bool RemovePage(XtraTabPage page);
|
||||
bool SelectPage(XtraTabPage page);
|
||||
bool ContainsPage(XtraTabPage page);
|
||||
}
|
||||
|
||||
public sealed class ModulePageEventArgs : EventArgs
|
||||
{
|
||||
public ModulePageEventArgs(XtraTabPage page)
|
||||
{
|
||||
Page = page;
|
||||
}
|
||||
|
||||
public XtraTabPage Page { get; private set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user