代码拉取完成,页面将自动刷新
using JoinBox.LabelBar.ViewModel;
using System.Windows.Threading;
using UserControl = System.Windows.Controls.UserControl;
using RadioButton = System.Windows.Controls.RadioButton;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using System.Windows.Controls;
namespace JoinBox.LabelBar.View;
// 此类将加载到一个同步上下文中
public partial class DocWindow : UserControl
{
public RelayCommand<DocElement> CreateCmd { set; get; }
public RelayCommand<DocElement> CloseCmd { set; get; }
public RelayCommand<DocElement> TabCmd { set; get; }
// 与界面绑定的会通过这个类
public DocumentTabData TabData;
#if cad
/// <summary>
/// 另存为之后立即关闭文档
/// </summary>
Document? _IsCloseDocument = null;
#endif
/// <summary>
/// 文档标签的WPF
/// </summary>
/// <param name="acapIntermediary">必须传参进来,而且要在同步线程</param>
public DocWindow()
{
// 点击按钮就会触发命令
TabData = new DocumentTabData();
TabCmd = new RelayCommand<DocElement>((info) => {
// 选中按钮
TabData.DocElementPick = info;
#if cad
AutoGo.Post(() => {
var doc = info.Document;
if (doc != null && !doc.IsActive && !doc.IsDisposed)
Acap.DocumentManager.MdiActiveDocument = doc;
});
#endif
});
CloseCmd = new RelayCommand<DocElement>(Post_CloseDwg);
CreateCmd = new RelayCommand<DocElement>((info) => {
#if cad
// 重置cad之后这两项的系统变量会弹窗,所以设置一下
AutoGo.Post(() => {
var docm = Acap.DocumentManager;
var doc = docm?.MdiActiveDocument;
if (doc != null)
{
var year = Env.GetAcadVersion();
if (year >= 2014)
Env.SetVar("secureload", 0); // 2014加载lisp不警告
Env.SetVar("acadlspasdoc", 1); // 将acad.lsp文件加载到每一个图形
}
});
Post_Qnew();
#endif
});
InitializeComponent();// 这里弹错表示去项目文件取消迭代版本号
DataContext = TabData;
Loaded += DocWindow_Loaded;
}
private void DocWindow_Loaded(object sender, RoutedEventArgs e)
{
#if !cad
// xaml是设计时,这是运行时
for (int i = 0; i < 6; i++)
{
var a = new DocElement("文档名示意" + i)
{
Document = new Document(),
};
TabData.DocElements.Add(a);
}
#else
Dm_DocumentCreated();
Acap.DocumentManager.DocumentCreated += Dm_DocumentCreated;
// 反应器->销毁前返回文档(先)
Acap.DocumentManager.DocumentToBeDestroyed += Dm_DocumentToBeDestroyed;
// 反应器->销毁前返回文档名(后)
Acap.DocumentManager.DocumentDestroyed += Dm_DocumentDestroyed;
// 反应器->否决命令
Acap.DocumentManager.DocumentLockModeChanged += Dm_VetoCommand;
// 反应器->切换文档就事件激活
Acap.DocumentManager.DocumentActivated += DocumentManager_DocumentActivated;
// 由于挤出的空间挡住了非最大化cad文档的空间,所以需要设置一下非最大化时候的文档.
AutoGo.Post(() => {
AnchorHelper.ReWindowSize();
});
#endif
}
#if cad
// https://spiderinnet1.typepad.com/blog/2012/08/autocad-net-close-documents-commandflags-modal-or-session.html
// https://through-the-interface.typepad.com/through_the_interface/2007/03/closing_all_ope.html
[CommandMethod(nameof(Cmd_CloseAndDiscard), CommandFlags.Session)]
public void Cmd_CloseAndDiscard()
{
var doc = Acap.DocumentManager.MdiActiveDocument;
if (doc.CommandInProgress != "")
doc.SendStringToExecute("\x03\x03", false, true, true);
if (doc.IsReadOnly)
{
doc.CloseAndDiscard();
return;
}
var dbmod = DBmodEx.DBmod;
if (dbmod == DBmod.DatabaseNoModifies)
doc.CloseAndDiscard();
else
doc.CloseAndSave(doc.Name);
}
public void CloseAndDiscardEx(Document doc)
{
// 0x01
// System.Reflection.TargetInvocationException:“调用的目标发生了异常。”
// COMException: 图形忙。
// 所以使用发送命令的方式,进行命令卡阻止,以免致命错误
// 0x02
// 多个关闭的时候,又存在图形忙,所以需要提前切换到此图再关闭.
ReplaceActiveDocument(doc);
doc.SendStringToExecute($"{nameof(Cmd_CloseAndDiscard)}\n", false, true, true);
}
#endif
/// <summary>
/// 关闭当前dwg
/// </summary>
/// <param name="info"></param>
private void Post_CloseDwg(DocElement info)
{
// 福萝卜用的是cad-com接口的saved可以判断是否已经保存
var doc = info.Document;
if (doc == null)
return;
#if cad
string file = string.Empty;
AutoGo.Post(() => {
ReplaceActiveDocument(doc);
// 如果数据库没有更改过,那么就直接关闭掉吧~
// 仅缩放时候:数据库没有改变,但是变量变了,视图变了,就关掉好了
var dbmod = DBmodEx.DBmod;
if (dbmod == DBmod.DatabaseNoModifies
||
(((dbmod & DBmod.Database) != DBmod.Database)
&& ((dbmod & DBmod.Value) == DBmod.Value)
&& ((dbmod & DBmod.View) == DBmod.View)))
{
CloseAndDiscardEx(doc);
return;
}
file = doc.Database.Filename;
// 自动保存时候产生的扩展名
// if (Path.GetExtension(file) == ".sv$")
// file = doc.Database.OriginalFileName;
// 如果是dwt就需要提示成dwg,然后另存为
// 如果是dwg就可以直接保存
file = Path.Combine(Path.GetDirectoryName(doc.Database.OriginalFileName), doc.Name);
// 数据库更改过但是没有保存过,就询问用户是否保存
var save = System.Windows.MessageBox.Show($"您已经修改过图纸,保存文档?\n\n{file}",
"惊惊盒子", MessageBoxButton.YesNoCancel);
if (save == MessageBoxResult.Cancel)
return;
if (save == MessageBoxResult.No)
{
CloseAndDiscardEx(doc);
return;
}
var originEx = Path.GetExtension(file).ToLower();
if (originEx == ".dwt")
{
// 用Qsave的话,按了保存,然后右上角的X此时就关闭了对话框和文档,造成丢失
// 而它就只会关闭了对话框,不会关闭文档
Post_SaveAs(info);
}
else if (originEx == ".dwg" || originEx == ".dxf")
{
if (!doc.IsReadOnly)// 只读文件
doc.CloseAndSave(file);
else
Post_SaveAs(info);
}
else
{
_IsCloseDocument = info.Document;
Post_Qsave(info);
}
});
#endif
}
#if cad
/// <summary>
/// 切换到当前文档
/// </summary>
/// <param name="doc"></param>
private static void ReplaceActiveDocument(Document doc)
{
// 如果不是当前文档,就切换过去,否则拿不到修改的变量
if (doc != null && doc != Acap.DocumentManager.MdiActiveDocument)
Acap.DocumentManager.MdiActiveDocument = doc;
}
/// <summary>
/// 获取dwg名称
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static string GetDocName(Document doc)
{
var file = doc.Database.Filename;
var ex = Path.GetExtension(file).ToLower();
if (!(ex == ".dwg" || ex == ".dxf"))
file = doc.Name;
return Path.GetFileNameWithoutExtension(file);
}
/// <summary>
/// 每次新建文档的自动执行
/// </summary>
public void Dm_DocumentCreated(object? sender = null, DocumentCollectionEventArgs? e = null)
{
Invoke(() => {
// cad08这个事件可能会重复两次,所以只能清空再加入
TabData.DocElements.Clear();
// 重建表格(防止tab占用Doc,所以加入名称)
foreach (Document doc in Acap.DocumentManager)
{
if (doc == null || doc.IsDisposed)// doc.IsActive初始化时候异常
continue;
var info = new DocElement(GetDocName(doc)) { Document = doc };
TabData.DocElements.Add(info);
}
});
// 第一个文档不会调用激活事件,所以这里要写Pick标签
if (e == null)
PickDocument();
}
/// <summary>
/// 选中当前活动文档的焦点
/// </summary>
/// <returns></returns>
private Document? PickDocument(Document? doca = null)
{
Document? pick = null;
if (doca != null)
{
pick = doca;
}
else
{
// 自运行一次时候,设置为激活的文档.
foreach (Document doc in Acap.DocumentManager)
{
if (doc == null || doc.IsDisposed)// doc.IsActive初始化时候异常
continue;
pick = doc;
break;
}
}
Invoke(() => {
if (pick != null)
TabData.InputPick = pick;// 新建了就激活为当前按钮
});
return pick;
}
/// <summary>
/// 反应器->切换文档就事件激活
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DocumentManager_DocumentActivated(object? sender = null, DocumentCollectionEventArgs? e = null)
{
// 切换文档标签激活
if (e != null)
PickDocument(e.Document);
}
/// <summary>
/// 反应器->销毁前返回文档(先)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Dm_DocumentToBeDestroyed(object? sender, DocumentCollectionEventArgs? e)
{
if (e == null)
return;
// 操作WPF(界面)是要线程安全,操作cad不用
Invoke(() => {
for (int i = TabData.DocElements.Count - 1; i >= 0; i--)
if (TabData.DocElements[i].Document == e.Document)
{
TabData.DocElements.RemoveAt(i);
break;
}
});
}
/// <summary>
/// 反应器->销毁前返回文档名(后)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Dm_DocumentDestroyed(object? sender, DocumentDestroyedEventArgs? e)
{
if (e == null)
return;
// 反应器->切换文档就事件激活也会设置焦点,所以此事件废弃.
// 重设焦点
// PickDocument();
}
/// <summary>
/// 反应器->命令否决触发命令前(不可锁文档)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Dm_VetoCommand(object sender, DocumentLockModeChangedEventArgs e)
{
if (e.GlobalCommandName == "" || e.GlobalCommandName == "#")
return;
var up = e.GlobalCommandName.ToUpper();
if (up == "#QSAVE" && _IsCloseDocument != null)
{
AutoGo.Post(() => {
CloseAndDiscardEx(_IsCloseDocument);
_IsCloseDocument = null;
});
}
if (up == "#QSAVE" || up == "#SAVEAS")
{
// 这两个命令都有可能变成另存为,而另存为时候用户可能修改文档名,
// 所以此处更改文档栏名称
foreach (var infoa in TabData.DocElements)
{
if (infoa.Document == null)
continue;
infoa.InfoDocName = GetDocName(infoa.Document);
infoa.Document = infoa.Document;//触发关联
}
}
// ctrl+q触发 _quit关闭时候
// 0x777CB922 (KernelBase.dll)处(位于 acad.exe 中)引发的异常: 0x000006C6: 数组绑定无效。。
if (up == "QUIT")
{
e.Veto(); // 否决命令
var info = TabData.DocElements.FirstOrDefault(item => item.Document == e.Document);
if (info != null)
Post_CloseDwg(info);
}
}
/// <summary>
/// 创建文档_自己写的版本_不可以改用发送消息版本,否则致命错误概率提高
/// </summary>
public static void Post_Qnew()
{
AutoGo.Post(CadCommand.QnewMethod);
}
/// <summary>
/// 打开_dwg
/// </summary>
public static void Post_OpenDwg()
{
// 因为当前文档必然存在,所以直接发送命令就好了
AutoGo.Post(() => {
var doc = Acap.DocumentManager.MdiActiveDocument;
doc.SendStringToExecute("_Open\n", false, true, true); // 不需要切换文档
});
}
/// <summary>
/// 保存_dwg
/// </summary>
public static void Post_Qsave(DocElement info)
{
// 因为当前文档必然存在,所以直接发送命令就好了
AutoGo.Post(() => {
info.Document?.SendStringToExecute("_Qsave\n", false, true, true); // 不需要切换文档
});
}
/// <summary>
/// 另存为_dwg
/// </summary>
/// <param name="info"></param>
public static void Post_SaveAs(DocElement info)
{
// 如果是只读状态,发送qsave是会提示只读,不会调用另存的,所以必须要一个另存为命令
AutoGo.Post(() => {
info.Document?.SendStringToExecute("_Saveas\n", false, true, true); // 不需要切换文档
});
}
#endif
/// <summary>
/// 控制线程是否作为WPF线程<br/>
/// net35为true,net35+为false
/// net35 必须要跑新线程上面<br/>
/// net35+ 也能用,但是会卡顿,卡顿发生在最大化和还原窗口/拖动窗口边界<br/>
/// 所以 net35+ 新建线程之后,通过post在主线程上面嵌入了<br/>
/// 原因是微软在 net35+ 上面修复了WPF的线程模型,<br/>
/// 所以可以直接利用背景线程进行嵌入<br/>
/// </summary>
public static bool DispatcherRunFlag =>
#if NET35
true;
#else
false;
#endif
/// <summary>
/// WPF界面线程安全
/// </summary>
/// <param name="ac"></param>
public void Invoke(Action ac)
{
if (DispatcherRunFlag)
{
// acad08利用新线程,所以是 Running
if (Dispatcher.Thread.ThreadState == System.Threading.ThreadState.Running)
Dispatcher.Invoke(ac);
else
Debug.WriteLine("DocWindow.ThreadState::" + Dispatcher.Thread.ThreadState);
}
else
{
// 高版本利用cad主窗口线程,所以说 Background
if (Dispatcher.Thread.ThreadState == System.Threading.ThreadState.Background)
Dispatcher.Invoke(ac);
else
Debug.WriteLine("DocWindow.ThreadState::" + Dispatcher.Thread.ThreadState);
}
}
/// <summary>
/// 右键菜单
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
#if cad
if (!(sender is System.Windows.Controls.MenuItem me
&& me.DataContext is DocElement info))
return;
switch (me.Header)
{
case "新建dwg":
Post_Qnew();
break;
case "打开dwg":
Post_OpenDwg();
break;
case "保存":
Post_Qsave(info);// 这是异步的,去拦截命令上面刷新一次
break;
case "另存":
Post_SaveAs(info);// 这是异步的,去拦截命令上面刷新一次
break;
case "全部保存":
foreach (var infoa in TabData.DocElements)
Post_Qsave(infoa);
break;
/**********************/
case "关闭":
Post_CloseDwg(info);
break;
case "关闭全部(会提示保存)":
foreach (var infoa in TabData.DocElements)
Post_CloseDwg(infoa);
break;
case "关闭全部(不保存)":
AutoGo.Post(Acap.DocumentManager.CloseAll);
break;
case "关闭所有其他图纸(保存)":
foreach (var item in this.TabData.DocElements)
if (item.Document != info.Document)
Post_CloseDwg(item);
break;
case "关闭所有其他图纸(不保存)":
AutoGo.Post(() => {
// 如果其他图纸中选择集状态下,进行此命令,
// 那么需要切换到当前,再发送命令才可以
foreach (Document doc in Acap.DocumentManager)
if (info.Document != doc)
CloseAndDiscardEx(doc);
});
break;
/**********************/
case "复制完整文件夹路径":
if (info.Document == null)
break;
System.Windows.Clipboard.SetText(info.Document.Database.Filename);
break;
case "打开文件的位置":
{
if (info.Document == null)
break;
// 不重复打开dwg路径的资源管理器(高版本把没有保存过的设置为不可用按钮)
var db = info.Document.Database;
var ex = Path.GetExtension(db.Filename).ToLower();
// 这里需要判断是否为只读
if (ex == ".dwg" || ex == ".dxf")
{
var wins = new ShellWindows();// 这个名字是为了兼容高版本(没想到吧
if (wins.Count > 0)
{
var dwgname = Env.GetVar("dwgname").ToString();
foreach (var item in wins)
if (item.LocationURL != null &&
item.LocationURL + "\\" + dwgname == db.Filename)
item.Quit();// 关闭
}
// 重开一个,防止选择状态被改变,
Process.Start("explorer", "/select,\"" + db.Filename + "\"");// 加引号防止空格中断
}
else
{
AutoGo.Post(() => {
if (info.Document is null)
return;
var ed = info.Document.Editor;
ed?.WriteMessage(Environment.NewLine + "你没有保存文件!\n");
});
}
}
break;
}
#endif
}
/// <summary>
/// 鼠标按下事件
/// 不要用MouseDown因为绑定了command之后就无法判断左键了,只能用PreviewMouseDown
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
// 中键关闭文档
if (e.MiddleButton == MouseButtonState.Pressed &&
e.Source is RadioButton ra2 &&
ra2.CommandParameter is DocElement info2)
Post_CloseDwg(info2);
}
System.Windows.Point mousePointBak = new();
bool canvas2_isMove = false;
/// <summary>
/// 鼠标左键按下(比命令绑定先运行)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// if (e.LeftButton == MouseButtonState.Pressed &&
// e.Source is RadioButton ra1 &&
// ra1.CommandParameter is Info info1)
// {
// }
// 获取鼠标
canvas2_isMove = true;
mousePointBak = e.GetPosition(null);
}
#if false
/// <summary>
/// 鼠标移动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_MouseMove(object sender, MouseEventArgs e)
{
Point p = Mouse.GetPosition(e.Source as FrameworkElement);
AutoGo.Print(sender);
if (e.LeftButton != MouseButtonState.Pressed)// 按着左键
return;
if (_JRadioButtonData == null)
return;
if (e.Source is RadioButton ra1 && ra1 != _JRadioButtonData.RadioButton)// 不相等就是交换,但是按着左键的的时候不切换e.Source
{
// 获取鼠标位置
var X = e.GetPosition(ra1).X;
var Y = e.GetPosition(ra1).Y;
var pt = new Point(X, Y);
// 偏移量
var offset = Math.Abs(_JRadioButtonData.Pt.X - pt.X);
if (offset != 0)
{
var DocElements2 = this.TabData.DocElements.ToList();
// 记录选中文档按钮的位置,然后清理所有按钮
int number = 0;
for (int i = 0; i < this.TabData.DocElements.Count - 1; i++)
{
if (this.TabData.DocElements[i] == _JRadioButtonData.Info)
{
number = i;
break;
}
}
this.TabData.DocElements.Clear();
if (offset > 0) // 往→
++number;
else // 往←
--number;
for (int i = 0; i < DocElements2.Count - 1; i++)
{
if (i == number)
this.TabData.DocElements.Add(_JRadioButtonData.Info);
else
this.TabData.DocElements.Add(DocElements2[i]);
}
}
}
}
#endif
// 窗体_鼠标抬起
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
{
canvas2_isMove = false;
// canvas2.Background = Brushes.Yellow;
}
// 窗体_鼠标移动
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (!canvas2_isMove)
return;
if (e.Source is not RadioButton canvas2)
return;
// canvas2.Background = Brushes.Red;
#if false
var currEle = sender as FrameworkElement;// 如果是窗体,那么这里会移动窗体
// 通过鼠标来控制_约束移动方向
double xPos = e.GetPosition(null).X - oldPoint.X + (double)currEle.GetValue(Canvas.LeftProperty);
currEle.SetValue(Canvas.LeftProperty, xPos);
// double yPos = e.GetPosition(null).Y - oldPoint.Y + (double)currEle.GetValue(Canvas.TopProperty);
// currEle.SetValue(Canvas.TopProperty, yPos);
#else
// 通过鼠标来控制_约束移动方向
double xPos = e.GetPosition(null).X - mousePointBak.X + (double)canvas2.GetValue(Canvas.LeftProperty);
canvas2.SetValue(Canvas.LeftProperty, xPos);
// double yPos = e.GetPosition(null).Y - oldPoint.Y + (double)canvas2.GetValue(Canvas.TopProperty);
// canvas2.SetValue(Canvas.TopProperty, yPos);
#endif
mousePointBak = e.GetPosition(null);
}
// https://www.cnblogs.com/luyingxue/articles/1280787.html
private void Btn_Gear_Click(object sender, RoutedEventArgs e)
{
if (JJStoryboard.GetIsPaused(this))
JJStoryboard.Resume(this);
else
JJStoryboard.Pause(this);
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。