新增托盘图标
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace Melskin.Controls
|
||||
}
|
||||
view.Refresh();
|
||||
|
||||
// !string.IsNullOrEmpty(Text) 的判断
|
||||
// !string.IsNullOrEmpty(Tip) 的判断
|
||||
if (!string.IsNullOrEmpty(Text) && !view.IsEmpty && textBox is { IsKeyboardFocused: true })
|
||||
{
|
||||
IsDropDownOpen = true;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Melskin.Controls.Decorations;
|
||||
/// CornerRadius="10"
|
||||
/// Background="#40FF0000">
|
||||
/// <!-- 半透明红色背景 -->
|
||||
/// <TextBlock Text = "Mini Card" Foreground="White" FontSize="16"/>
|
||||
/// <TextBlock Tip = "Mini Card" Foreground="White" FontSize="16"/>
|
||||
///</GlassChromeDecorator>
|
||||
/// </code>
|
||||
/// <![CDATA[]]>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Melskin.Controls;
|
||||
@@ -193,7 +192,7 @@ public class MelWindow : Window
|
||||
if (maximizeRestoreButton is Visual maximizeButtonVisual)
|
||||
{
|
||||
var bounds = VisualTreeHelper.GetDescendantBounds(maximizeButtonVisual);
|
||||
Debug.WriteLine(bounds.ToString());
|
||||
//Debug.WriteLine(bounds.ToString());
|
||||
var buttonTransform = maximizeButtonVisual.TransformToAncestor(this);
|
||||
var buttonRect = buttonTransform.TransformBounds(bounds);
|
||||
|
||||
@@ -228,7 +227,7 @@ public class MelWindow : Window
|
||||
//VisualStateManager.GoToState(maximizeRestoreButton, "MouseOver", true);
|
||||
new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1A000000")),
|
||||
"Normal" => Brushes.Transparent,
|
||||
//VisualStateManager.GoToState(maximizeRestoreButton, "Normal", true);
|
||||
//VisualStateManager.GoToState(maximizeRestoreButton, "Normal", true);
|
||||
_ => maximizeRestoreButton?.Background
|
||||
};
|
||||
}
|
||||
@@ -322,7 +321,7 @@ public class MelWindow : Window
|
||||
/// <inheritdoc />
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
base.OnApplyTemplate();
|
||||
minimizeButton?.Click -= MinimizeButtonClickHandler;
|
||||
|
||||
minimizeButton = GetTemplateChild(VbMinimizeButtonName) as Button;
|
||||
|
||||
@@ -83,7 +83,7 @@ public partial class ToastControl
|
||||
// foreground = new SolidColorBrush(Color.FromRgb(245, 108, 108));
|
||||
// break;
|
||||
// }
|
||||
// control.IconTextBlock.Text = icon;
|
||||
// control.IconTextBlock.Tip = icon;
|
||||
// control.IconTextBlock.Foreground = foreground;
|
||||
// control.RootBorder.Background = background;
|
||||
// control.RootBorder.BorderBrush = background;
|
||||
|
||||
216
Melskin/Controls/TrayIcon.cs
Normal file
216
Melskin/Controls/TrayIcon.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Melskin.Controls
|
||||
{
|
||||
public class TrayIcon : FrameworkElement, IDisposable
|
||||
{
|
||||
// 1. 悬浮提示文本
|
||||
public static readonly DependencyProperty TipProperty =
|
||||
DependencyProperty.Register(nameof(Tip), typeof(string), typeof(TrayIcon),
|
||||
new PropertyMetadata(string.Empty, OnTextChanged));
|
||||
|
||||
public string Tip
|
||||
{
|
||||
get => (string)GetValue(TipProperty);
|
||||
set => SetValue(TipProperty, value);
|
||||
}
|
||||
|
||||
// 2. 双击命令
|
||||
public static readonly DependencyProperty DoubleClickCommandProperty =
|
||||
DependencyProperty.Register(nameof(DoubleClickCommand), typeof(ICommand), typeof(TrayIcon));
|
||||
|
||||
public ICommand DoubleClickCommand
|
||||
{
|
||||
get => (ICommand)GetValue(DoubleClickCommandProperty);
|
||||
set => SetValue(DoubleClickCommandProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ClickCommandProperty =
|
||||
DependencyProperty.Register(nameof(ClickCommand), typeof(ICommand), typeof(TrayIcon));
|
||||
|
||||
public ICommand ClickCommand
|
||||
{
|
||||
get => (ICommand)GetValue(ClickCommandProperty);
|
||||
set => SetValue(ClickCommandProperty, value);
|
||||
}
|
||||
|
||||
// 3. 【新增】图标本地路径 (例如: "app.ico")
|
||||
public static readonly DependencyProperty IconPathProperty =
|
||||
DependencyProperty.Register(nameof(IconPath), typeof(string), typeof(TrayIcon),
|
||||
new PropertyMetadata(string.Empty, OnIconPathChanged));
|
||||
|
||||
public string IconPath
|
||||
{
|
||||
get => (string)GetValue(IconPathProperty);
|
||||
set => SetValue(IconPathProperty, value);
|
||||
}
|
||||
|
||||
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var ctrl = (TrayIcon)d;
|
||||
if (ctrl._isAdded)
|
||||
{
|
||||
ctrl._nid.szTip = e.NewValue?.ToString() ?? "";
|
||||
Shell_NotifyIcon(NIM_MODIFY, ref ctrl._nid);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIconPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var ctrl = (TrayIcon)d;
|
||||
if (ctrl._isAdded)
|
||||
{
|
||||
ctrl._nid.hIcon = ctrl.LoadMyIcon(e.NewValue?.ToString());
|
||||
Shell_NotifyIcon(NIM_MODIFY, ref ctrl._nid);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Win32 API 声明 ---
|
||||
private const int NIM_ADD = 0x0000;
|
||||
private const int NIM_MODIFY = 0x0001;
|
||||
private const int NIM_DELETE = 0x0002;
|
||||
private const int NIF_MESSAGE = 0x0001;
|
||||
private const int NIF_ICON = 0x0002;
|
||||
private const int NIF_TIP = 0x0004;
|
||||
private const int WM_LBUTTONUP = 0x0202; // 左键弹起消息
|
||||
private const int WM_TRAYMOUSEMESSAGE = 0x0400 + 1024;
|
||||
private const int WM_LBUTTONDBLCLK = 0x0203;
|
||||
private const int WM_RBUTTONUP = 0x0205; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct NOTIFYICONDATA
|
||||
{
|
||||
public int cbSize;
|
||||
public IntPtr hwnd;
|
||||
public int uID;
|
||||
public int uFlags;
|
||||
public int uCallbackMessage;
|
||||
public IntPtr hIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
public string szTip;
|
||||
public int dwState;
|
||||
public int dwStateMask; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||
public string szInfo;
|
||||
public int uTimeoutOrVersion;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string szInfoTitle;
|
||||
public int dwInfoFlags;
|
||||
public Guid guidItem;
|
||||
public IntPtr hBalloonIcon;
|
||||
}
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA pnid);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
// 【新增】纯 Win32 加载本地图标 API
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr LoadImage(IntPtr hInst, string lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad);
|
||||
|
||||
private IntPtr LoadMyIcon(string iconPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(iconPath)) return IntPtr.Zero;
|
||||
// 1 = IMAGE_ICON, 0x00000010 = LR_LOADFROMFILE
|
||||
return LoadImage(IntPtr.Zero, iconPath, 1, 0, 0, 0x00000010);
|
||||
}
|
||||
|
||||
// --- 内部状态 ---
|
||||
private HwndSource _messageSink;
|
||||
private int _uID = 100;
|
||||
private bool _isAdded = false;
|
||||
private NOTIFYICONDATA _nid;
|
||||
|
||||
public TrayIcon()
|
||||
{
|
||||
Visibility = Visibility.Collapsed;
|
||||
this.Loaded += OnLoaded;
|
||||
this.Unloaded += OnUnloaded;
|
||||
if (Application.Current != null)
|
||||
Application.Current.Exit += (s, e) => Dispose();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isAdded) return;
|
||||
|
||||
HwndSourceParameters p = new HwndSourceParameters("TrayIconMessageSink")
|
||||
{
|
||||
Width = 0, Height = 0, WindowStyle = 0, ExtendedWindowStyle = 0x00000080
|
||||
};
|
||||
_messageSink = new HwndSource(p);
|
||||
_messageSink.AddHook(WndProc);
|
||||
|
||||
_nid = new NOTIFYICONDATA
|
||||
{
|
||||
cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)),
|
||||
hwnd = _messageSink.Handle,
|
||||
uID = _uID,
|
||||
uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON,
|
||||
uCallbackMessage = WM_TRAYMOUSEMESSAGE,
|
||||
szTip = this.Tip ?? "",
|
||||
// 加载属性中指定的图标
|
||||
hIcon = LoadMyIcon(this.IconPath)
|
||||
};
|
||||
|
||||
Shell_NotifyIcon(NIM_ADD, ref _nid);
|
||||
_isAdded = true;
|
||||
}
|
||||
|
||||
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (msg == WM_TRAYMOUSEMESSAGE)
|
||||
{
|
||||
int mouseMsg = lParam.ToInt32();
|
||||
if (mouseMsg == WM_LBUTTONUP)
|
||||
{
|
||||
if (ClickCommand?.CanExecute(null) == true)
|
||||
{
|
||||
ClickCommand.Execute(null);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (mouseMsg == WM_LBUTTONDBLCLK)
|
||||
{
|
||||
if (DoubleClickCommand?.CanExecute(null) == true)
|
||||
DoubleClickCommand.Execute(null);
|
||||
handled = true;
|
||||
}
|
||||
else if (mouseMsg == WM_RBUTTONUP)
|
||||
{
|
||||
ShowContextMenu();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void ShowContextMenu()
|
||||
{
|
||||
if (this.ContextMenu != null)
|
||||
{
|
||||
SetForegroundWindow(_messageSink.Handle);
|
||||
this.ContextMenu.PlacementTarget = null;
|
||||
this.ContextMenu.Placement = PlacementMode.MousePoint;
|
||||
this.ContextMenu.IsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e) => Dispose();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isAdded)
|
||||
{
|
||||
Shell_NotifyIcon(NIM_DELETE, ref _nid);
|
||||
_isAdded = false;
|
||||
}
|
||||
if (_messageSink != null)
|
||||
{
|
||||
_messageSink.RemoveHook(WndProc);
|
||||
_messageSink.Dispose();
|
||||
_messageSink = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Melskin.Markup;
|
||||
/// 使用此扩展时,需要确保已正确引用了Material Design图标字体资源。可以通过设置`FontFamily`属性来应用图标字体。
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code lang="xml"> <TextBlock Text="{enu:SymbolIcon Symbol=AddBox}" FontFamily="{StaticResource MaterialIconFont}"/>
|
||||
/// <code lang="xml"> <TextBlock Tip="{enu:SymbolIcon Symbol=AddBox}" FontFamily="{StaticResource MaterialIconFont}"/>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[ContentProperty(nameof(Symbol))]
|
||||
|
||||
@@ -120,6 +120,27 @@
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="24" />
|
||||
</Grid.RowDefinitions>
|
||||
<ms:TrayIcon
|
||||
x:Name="MyTray"
|
||||
ClickCommand="{Binding ShowWindowCommand}"
|
||||
DoubleClickCommand="{Binding ShowWindowCommand}"
|
||||
IconPath="Resources/Images/authentication.ico"
|
||||
Tip="我的WPF程序托盘">
|
||||
|
||||
<!-- 原生的 WPF 右键菜单 -->
|
||||
<ms:TrayIcon.ContextMenu>
|
||||
<ContextMenu>
|
||||
<!-- 支持普通的 Click 事件 -->
|
||||
<MenuItem
|
||||
Click="MenuShow_Click"
|
||||
FontWeight="Bold"
|
||||
Header="显示主界面" />
|
||||
<Separator />
|
||||
<MenuItem Click="MenuExit_Click" Header="完全退出" />
|
||||
</ContextMenu>
|
||||
</ms:TrayIcon.ContextMenu>
|
||||
|
||||
</ms:TrayIcon>
|
||||
<!-- 工具栏 -->
|
||||
<ToolBarTray Margin="4,4,4,0">
|
||||
<ToolBar Foreground="{DynamicResource PrimaryFocusedBrush}" Header="工具栏">
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -82,6 +80,7 @@ public partial class MainWindow
|
||||
#region 命令
|
||||
public ICommand ShowSelectedItemsCommand { get; set; }
|
||||
public ICommand ToggleDetailsCommand { get; set; }
|
||||
public ICommand ShowWindowCommand { get; set; }
|
||||
|
||||
[RelayCommand]
|
||||
private void AddArea()
|
||||
@@ -227,6 +226,7 @@ public partial class MainWindow
|
||||
{
|
||||
ShowSelectedItemsCommand = new RelayCommand(ShowSelectedItems);
|
||||
ToggleDetailsCommand = new RelayCommand<PersonItem>(ToggleDetails);
|
||||
ShowWindowCommand = new RelayCommand(ShowWindow);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -456,12 +456,38 @@ public partial class MainWindow
|
||||
case 2:
|
||||
MyTutorial.ShowStep(StartBtn, "删除功能", "谨慎使用,这会清除数据。", "2 / 2");
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
MyTutorial.Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ShowWindow()
|
||||
{
|
||||
this.Show();
|
||||
this.WindowState = WindowState.Normal;
|
||||
this.Activate(); // 激活窗口使其置顶
|
||||
}
|
||||
// --- 2. 右键菜单的 Click 事件 (Code-behind 风格) ---
|
||||
private void MenuShow_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowWindow();
|
||||
}
|
||||
|
||||
private void MenuExit_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 真正退出程序
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
// --- 3. 重写关闭事件,实现“点X最小化到托盘” ---
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
// 阻止窗口真正销毁
|
||||
e.Cancel = true;
|
||||
// 隐藏窗口(此时托盘图标依然在运行)
|
||||
this.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
#region 数据模型
|
||||
|
||||
@@ -34,5 +34,10 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Resources\Images\authentication.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
BIN
MelskinTest/Resources/Images/authentication.ico
Normal file
BIN
MelskinTest/Resources/Images/authentication.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -1,12 +1,12 @@
|
||||
using Autodesk.Revit.Attributes;
|
||||
|
||||
using Nice3point.Revit.Toolkit.External;
|
||||
|
||||
using ShrlAlgoToolkit.RevitAddins.Common.Assists;
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.ModelManager;
|
||||
|
||||
[Transaction(TransactionMode.Manual)]
|
||||
[UsedImplicitly]
|
||||
public class ModelCheckCmd : ExternalCommand
|
||||
{
|
||||
public override void Execute() => WinDialogAssist.ShowOrActivate<ModelCheckView, ModelCheckViewModel>(UiApplication);//ModelCheckView view = SingletonViewAssist<ModelCheckView>.GetInstance(out var isNewCreate);//if (isNewCreate)//{// view.DataContext = new ModelCheckViewModel(uiApplication);// view.ShowAhead();//}//view.Activate();
|
||||
|
||||
@@ -23,7 +23,6 @@ using ShrlAlgoToolkit.RevitAddins.Common.Controls;
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.ModelManager;
|
||||
|
||||
[UsedImplicitly]
|
||||
public partial class ModelCheckViewModel : ObservableObject
|
||||
{
|
||||
private readonly Standardizer.CorrectReferLevelExecutes correctReferLevelExecutes;
|
||||
@@ -116,13 +115,13 @@ public partial class ModelCheckViewModel : ObservableObject
|
||||
var errorItem = new MessageModel(type, "类型名称不符合三段式");
|
||||
Items.Add(errorItem);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (IsCheckProps)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
if (IsCheckSlope)
|
||||
{
|
||||
|
||||
@@ -3,48 +3,7 @@ using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitAddins.Common.Extensions;
|
||||
#region 格式化
|
||||
|
||||
//StringFormat={}{0:C} $123.46
|
||||
//StringFormat={}{0:C1} $123.5
|
||||
//StringFormat=单价:{0:C} 单价:$123.46
|
||||
//StringFormat={}{0}元 123.45678元
|
||||
//StringFormat={}{0:D6} 086723
|
||||
//StringFormat={}{0:F4} 28768234.9329
|
||||
//StringFormat={}{0:N3} 28,768,234.933
|
||||
//StringFormat={}{0:P1} 78.9 %
|
||||
//StringFormat={}{0:0000.00} 0123.46
|
||||
//StringFormat={}{0:####.##} 123.46
|
||||
//StringFormat={}{0:d} 5/4/2015
|
||||
//StringFormat={}{0:D} Monday, May 04, 2015
|
||||
//StringFormat={}{0:f} Monday, May 04, 2015 5:46 PM
|
||||
//StringFormat={}{0:F} Monday, May 04, 2015 5:46:56 PM
|
||||
//StringFormat={}{0:g} 5/4/2015 5:46 PM
|
||||
//StringFormat={}{0:G} 5/4/2015 5:46:56 PM
|
||||
//StringFormat={}{0:m} May 04
|
||||
//StringFormat={}{0:Distinct} May 04
|
||||
//StringFormat={}{0:t} 5:46 PM
|
||||
//StringFormat={}{0:Command} 5:46:56 PM
|
||||
//StringFormat={}{0:yyyy年MM月dd日} 2015年05月04日
|
||||
//StringFormat={}{0:yyyy-MM-dd} 2015-05-04
|
||||
//StringFormat={}{0:yyyy-MM-dd HH:mm} 2015-05-04 17:46
|
||||
//StringFormat={}{0:yyyy-MM-dd HH:mm:ss},,ConverterCulture=zh-CN||StringFormat='yyyy:MM:dd HH:mm:ss',,ConverterCulture=zh-CN 2015-05-04 17:46:56
|
||||
//< TextBox.Text >
|
||||
// < MultiBinding StringFormat = "姓名:{0}	{1}" >
|
||||
// < Binding Path = "FristName" />
|
||||
// < Binding Path = "LastName" />
|
||||
// </ MultiBinding >
|
||||
//</ TextBox.Text >
|
||||
// < !--
|
||||
// \a  BEL
|
||||
// \b  BS - Backspace
|
||||
// \f  FF - Formfeed
|
||||
// \n 
 LF, NL - Linefeed, New Line
|
||||
// \r 
 CR - Carriage return
|
||||
// \t 	 HT - Tab, Horizontal Tabelator
|
||||
// \v  VT - Vertical Tabelator
|
||||
|
||||
#endregion
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -297,5 +256,5 @@ public static class StringExtensions
|
||||
? rv
|
||||
: Convert.ToInt32(StrToFloat(expression, defValue));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public class RequiredValidationRule : ValidationRule
|
||||
// InputMethod.IsInputMethodEnabled= "False"
|
||||
// md:HintAssist.Hint= "楼板偏移"
|
||||
// md:TextFieldAssist.SuffixText= "mm" >
|
||||
// < TextBox.Text >
|
||||
// < TextBox.Tip >
|
||||
// < Binding
|
||||
// Path= "FloorOffset"
|
||||
// StringFormat= "{}{0:N2}"
|
||||
@@ -23,7 +23,7 @@ public class RequiredValidationRule : ValidationRule
|
||||
// <domain:RangeValidationRule Max = "100" Min= "0" xmlns:domain= "clr-namespace:Sai.Toolkit.Mvvm.ValidationRules" ValidatesOnTargetUpdated = "True" />
|
||||
// </ Binding.ValidationRules >
|
||||
// </ Binding >
|
||||
// </ TextBox.Text >
|
||||
// </ TextBox.Tip >
|
||||
// </ TextBox >
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@ public class DrawingViewApp
|
||||
// LargeImage = Resources.zoom_32px.ToBitmapSource(),
|
||||
// Size = RibbonItemSize.Large,
|
||||
// Name = "ZoomElement",
|
||||
// Text = "最大化",
|
||||
// Tip = "最大化",
|
||||
// ShowText = true,
|
||||
// ToolTip = "根据视图的图元是否可见,在切换视图时,对当前选中的图元进行快速缩放定位",
|
||||
// IsCheckable = true,
|
||||
|
||||
@@ -359,7 +359,7 @@ public partial class FamilyLibraryViewModel : ObservableObject
|
||||
private void SearchFamily(object obj)
|
||||
{
|
||||
//ListViewItem foundItem =
|
||||
// textListView.FindItemWithText(searchBox.Text, false, 0, true);
|
||||
// textListView.FindItemWithText(searchBox.Tip, false, 0, true);
|
||||
//if (foundItem != null)
|
||||
//{
|
||||
// textListView.TopItem = foundItem;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
public class CodeExecutor
|
||||
{
|
||||
public void Execute(string methodBody, Autodesk.Revit.DB.Document doc, UIDocument uidoc)
|
||||
{
|
||||
string code = $@"
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.UI;
|
||||
using System;
|
||||
|
||||
public class ScriptRunner
|
||||
{{
|
||||
public void Run(Document doc, UIDocument uidoc)
|
||||
{{
|
||||
{methodBody}
|
||||
}}
|
||||
}}";
|
||||
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(code);
|
||||
|
||||
var compilation = CSharpCompilation.Create(
|
||||
"DynamicScript")
|
||||
.AddReferences(
|
||||
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
||||
MetadataReference.CreateFromFile(typeof(Document).Assembly.Location))
|
||||
.AddSyntaxTrees(syntaxTree)
|
||||
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
var result = compilation.Emit(ms);
|
||||
|
||||
if (!result.Success)
|
||||
throw new Exception("Compilation failed");
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var assembly = Assembly.Load(ms.ToArray());
|
||||
|
||||
var type = assembly.GetType("ScriptRunner");
|
||||
var instance = Activator.CreateInstance(type);
|
||||
type.GetMethod("Run").Invoke(instance, new object[] { doc, uidoc });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
internal interface ILLMProvider
|
||||
{
|
||||
Task GenerateStreamAsync(string systemPrompt,
|
||||
string userPrompt,
|
||||
Action<string> onTokenReceived);
|
||||
}
|
||||
}
|
||||
40
ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs
Normal file
40
ShrlAlgoToolkit.RevitAddins/General/LLMScript/LLMCmd.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Text;
|
||||
|
||||
using Autodesk.Revit.Attributes;
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
using Nice3point.Revit.Toolkit.External;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
/// <summary>
|
||||
/// Revit执行命令
|
||||
/// </summary>
|
||||
[Transaction(TransactionMode.Manual)]
|
||||
[Regeneration(RegenerationOption.Manual)]
|
||||
public class LLMCmd : ExternalCommand
|
||||
{
|
||||
public async override void Execute()
|
||||
{
|
||||
string userTask = "将当前选中墙的Comments参数追加'检查'";
|
||||
|
||||
PromptManager promptManager = new PromptManager("Prompts/System_UltraStrict.txt");
|
||||
string systemPrompt = promptManager.GetSystemPrompt();
|
||||
string userPrompt = promptManager.BuildUserPrompt(userTask);
|
||||
|
||||
var llm = new OllamaProvider("http://localhost:11434/api/chat", "revit-coder");
|
||||
StringBuilder fullCode = new StringBuilder();
|
||||
|
||||
await llm.GenerateStreamAsync(
|
||||
systemPrompt,
|
||||
userPrompt,
|
||||
chunk =>
|
||||
{
|
||||
fullCode.Append(chunk);
|
||||
});
|
||||
CodeExecutor executor = new CodeExecutor();
|
||||
|
||||
executor.Execute(fullCode.ToString(), Document, UiDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
public class OllamaProvider : ILLMProvider
|
||||
{
|
||||
private static readonly HttpClient _client = new HttpClient();
|
||||
private readonly string _endpoint;
|
||||
private readonly string _model;
|
||||
|
||||
public OllamaProvider(string endpoint, string model)
|
||||
{
|
||||
_endpoint = endpoint;
|
||||
_model = model;
|
||||
}
|
||||
|
||||
public async Task GenerateStreamAsync(
|
||||
string systemPrompt,
|
||||
string userPrompt,
|
||||
Action<string> onTokenReceived)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
model = _model,
|
||||
messages = new[]
|
||||
{
|
||||
new { role = "system", content = systemPrompt },
|
||||
new { role = "user", content = userPrompt }
|
||||
},
|
||||
stream = true
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(payload);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, _endpoint);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _client.SendAsync(
|
||||
request,
|
||||
HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = await reader.ReadLineAsync();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
JObject obj = JObject.Parse(line);
|
||||
|
||||
bool done = obj["done"]?.Value<bool>() ?? false;
|
||||
|
||||
var content = obj["message"]?["content"];
|
||||
if (content != null)
|
||||
{
|
||||
onTokenReceived(content.ToString());
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
public class OpenAiProvider : ILLMProvider
|
||||
{
|
||||
private static readonly HttpClient _client = new HttpClient();
|
||||
private readonly string _endpoint;
|
||||
private readonly string _apiKey;
|
||||
private readonly string _model;
|
||||
|
||||
public OpenAiProvider(string endpoint, string apiKey, string model)
|
||||
{
|
||||
_endpoint = endpoint;
|
||||
_apiKey = apiKey;
|
||||
_model = model;
|
||||
|
||||
_client.DefaultRequestHeaders.Clear();
|
||||
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
|
||||
}
|
||||
|
||||
public async Task GenerateStreamAsync(
|
||||
string systemPrompt,
|
||||
string userPrompt,
|
||||
Action<string> onTokenReceived)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
model = _model,
|
||||
messages = new[]
|
||||
{
|
||||
new { role = "system", content = systemPrompt },
|
||||
new { role = "user", content = userPrompt }
|
||||
},
|
||||
temperature = 0,
|
||||
stream = true
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(payload);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, _endpoint);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _client.SendAsync(
|
||||
request,
|
||||
HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = await reader.ReadLineAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
if (!line.StartsWith("data:"))
|
||||
continue;
|
||||
|
||||
string data = line.Substring(5).Trim();
|
||||
|
||||
if (data == "[DONE]")
|
||||
break;
|
||||
|
||||
JObject obj = JObject.Parse(data);
|
||||
var delta = obj["choices"]?[0]?["delta"]?["content"];
|
||||
|
||||
if (delta != null)
|
||||
{
|
||||
onTokenReceived(delta.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Szmedi.RvKits.LLMScript
|
||||
{
|
||||
public class PromptManager
|
||||
{
|
||||
private readonly string _systemPrompt;
|
||||
|
||||
public PromptManager(string promptPath)
|
||||
{
|
||||
_systemPrompt = File.ReadAllText(promptPath);
|
||||
}
|
||||
|
||||
public string GetSystemPrompt()
|
||||
{
|
||||
return _systemPrompt;
|
||||
}
|
||||
|
||||
public string BuildUserPrompt(string task)
|
||||
{
|
||||
return task.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
global using Autodesk.Revit.UI;
|
||||
global using JetBrains.Annotations;
|
||||
|
||||
global using ShrlAlgoToolkit.RevitCore.Assists;
|
||||
global using ShrlAlgoToolkit.RevitCore.Extensions;
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
<!-- <Reference Include="Microsoft.CSharp" Condition="'$(RevitVersion)' == '2018' Or '$(RevitVersion)' == '2019' Or '$(RevitVersion)' == '2020'" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" Condition="'$(RevitVersion)' == '2018' Or '$(RevitVersion)' == '2019' Or '$(RevitVersion)' == '2020' " />-->
|
||||
<Reference Include="eTransmitForRevitDB" HintPath="..\libs\$(RevitVersion)\eTransmitForRevitDB.dll" />
|
||||
<Reference Include="System.Net.Http">
|
||||
<HintPath>C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<!-- <Reference Include="RevitAPIIFC" Condition=" '$(RevitVersion)' == '2025' " HintPath="..\libs\$(RevitVersion)\RevitAPIIFC.dll" Private="False" />
|
||||
<Reference Include="UIFramework" HintPath="..\libs\$(RevitVersion)\UIFramework.dll" Private="False" />
|
||||
<Reference Include="UIFrameworkServices" HintPath="..\libs\$(RevitVersion)\UIFrameworkServices.dll" Private="False" />-->
|
||||
@@ -101,9 +104,11 @@
|
||||
<PackageReference Include="ACadSharp" Version="3.4.9" />
|
||||
<PackageReference Include="FuzzySharp" Version="2.0.2" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||
<!--TreatAsUsed="true"标记为已使用-->
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" TreatAsUsed="True" />
|
||||
<PackageReference Include="Nice3point.Revit.Extensions" Version="2020.3.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<!--<PackageReference Include="Nice3point.Revit.Extensions" Version="2020.3.0" />-->
|
||||
<PackageReference Include="Nice3point.Revit.Toolkit" Version="2020.*" />
|
||||
|
||||
<PackageReference Include="MiniExcel" Version="1.42.0" />
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
namespace ShrlAlgoToolkit.RevitCore.Base
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user