Σ( ° △ °** | **) |
使用 WPF + Material Design 构建应用
主页 Material Design In XAML Toolkit
其他选择
mahapps.metro——a UI toolkit for WPF
发挥想象
一些技巧
圆角窗口
<Window ...
WindowStyle="None" Background="Transparent" AllowsTransparency="True">
<Border Background="White" CornerRadius="15,15,15,15">
<Grid>
<!--主界面-->
</Grid>
<Border/>
</Window>
-
原理比较简单,首先在
标签添加三个属性,之后在根布局(上述代码是Grid)外添加 即可
改变 ToolBar 方向(利用ToolBarTray)
<ToolBarTray x:Name="toolBarTray" Orientation="Vertical">
<ToolBar>
</ToolBar>
</ToolBarTray>
动态向Toolbar添加按钮,按钮实现右键菜单动态删除自身效果
private void AddButton_Click(object sender, RoutedEventArgs e)
{
Button button = new Button();
button.Name = "BUTTON_NAME";
button.Content = button.Name;
MenuItem deleteMenu = new MenuItem //新建一个菜单子项
{
Header = "删除",
FontFamily = new FontFamily("微软雅黑"),
FontWeight = FontWeights.Bold,
};
deleteMenu.Click += DeleteMenu_Click; //alt+enter自动生成待实现方法
button.ContextMenu = new ContextMenu(); //添加右键菜单
button.ContextMenu.Items.Add(deleteMenu); //菜单子项添加进按钮右键菜单
this.ToolBar.Items.Add(button);
this.ToolBar.RegisterName(button.Name, button);//注册之后可以通过findName找到目标控件
}
private void DeleteMenu_Click(object sender, RoutedEventArgs e)
{
var mi = sender as MenuItem;
var cm = mi.Parent as ContextMenu;
var button = cm.PlacementTarget as Button;
this.ToolBar.UnregisterName(button.Name);
this.ToolBar.Items.Remove(button);
}
Tip:通过右键删除自身的关键是获取到右键菜单源控件的引用,值得一提的是 DeleteMenu_Click 方法中获取源控件的方式并不通用,假设你通过 Toolbar 自动给每一个子项添加右键菜单,上述代码 PlacementTarget 只能获取到 ToolBar 的引用。一种更加通用的做法是为按钮添加一个 MouseDown 事件(在对按钮做任何按键动作都会触发此方法,为什么不是 RightMouseDown 事件因为它会拦截右键的动作而不会触发右键菜单),此时获取按钮的引用(sender as Button)存储到一个固定的位置(例如父容器 ToolBar.Tag 中),当触发右键菜单点击事件时就可以获取到源控件引用,不过要确保那个位置不会被其他代码使用。
划分网格+填充图片
//2X2网格
RowDefinition row1 = new RowDefinition();
RowDefinition row2 = new RowDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
GRID_ID.RowDefinitions.Add(row1);
GRID_ID.RowDefinitions.Add(row2);
GRID_ID.ColumnDefinitions.Add(col1);
GRID_ID.ColumnDefinitions.Add(col2);
GRID_ID.ShowGridLines = true;
Image image1 = new Image
{
Source = PictureURI(@"IMAGE_PATH"),
Stretch = Stretch.Fill, //图片拉伸至控件大小
Margin = new Thickness(6),
};
image1.SetValue(Grid.RowProperty, 0);
image1.SetValue(Grid.ColumnProperty, 0);
GRID_ID.Children.Add(image1);
- 如果想自定义行或列的尺寸(比例关系/绝对大小等等),可以参考这篇文章《设定 Grid 行或列的尺寸》(出自《WPF 专业编程指南》3.5.2章节),通过 GridLength 指定 GridUnitType 即可设定
- 指定图片在 Image 控件的表现形式(正常、保持比例、填充、拉伸),可指定 Stretch 值
- 如果对元素定位不太擅长可以看《Alignment、Margin 和 Padding 概述》 这篇文章
- 删除网格中的所有内容和网格行列定义看这篇文章:如何清除 WPF Grid 内容?
- 参考了 《WPF 向 Grid 中动态添加控件》 和 《WPF-使用代码创建 Grid 行与列,并将控件添加到 Grid 中的指定行指定列》 文章
在 Canvas 中添加一个可拖拽移动的控件
<Grid Name="EMap_Container" Grid.Column="1">
<Canvas Name="EMapArea">
<!--绑定Canvas父元素容器长宽属性使得Canvas中的背景元素大小可以自适应-->
<Grid Width="{Binding ActualWidth, ElementName=EMap_Container}"
Height="{Binding ActualHeight, ElementName=EMap_Container}">
<!--ZIndex=-1作为背景-->
<Image Panel.ZIndex="-1"
Source=@"\Source\Image.jpg"
Stretch="Fill"/>
</Grid>
</Canvas>
</Grid>
private void EMap_Button_Cctv_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
Image image = new Image {
Width = 30, Height = 30,
Source = new BitmapImage(new Uri(@"\Source\Icon\Cam.gif"))
};
//设置元素在Canvas中的位置
Canvas.SetLeft(image, 20);
Canvas.SetTop(image, 20);
//元素赋予可拖拽移动的behavior
Interaction.GetBehaviors(image).Add(
new MouseDragElementBehavior() { ConstrainToParentBounds = true });
EMapArea.Children.Add(image);
}
}
- 在Blend-资产中有很多 behavior 可以利用,不过首先在项目中要添加 Microsoft.Expression.Interactions 和 System.Windows.Interactivity 两个引用
- 参考 【①】How to Add Behaviors programmatically、【②】Attach behavior in code behind、【③】WPF Tutorial Canvas 控件、【④】How to set x, y coordinates of WPF canvas children through code?
参考列表
合理分层
避免循环依赖(职责分配)
设计先行
实现现行
良好的API
使用 IOC 维护实例
持久化你的数据
数据库不是唯一选择
小心编码
使用Linq to SQL 实现 CRUD
在 VisualStudio 中使用 GIT 进行源码管理
参考列表
单元测试
创建单元测试
使用日志
查看异常信息
单元测试不总是顺利的,一般而言都会出现以下“常见的”提示框(NullReferenceException真程序员噩梦)
我们需要定位异常位置,点击查看详细信息
展开$exception,找到StackTrace,点击右侧的放大镜按钮,就可以阅读栈信息。
根据栈信息定位到问题代码(不过IDEA就直接可以看异常栈,VS也应该可以,不过没找到位置)
勘测现场——自动窗口
尽可能利用框架提供的一切
有关WPF生命周期与钩子方法
不需要记住,当需要的时候只需查看Window类定义即可
参考列表
利用 Windows API 进行身份验证
如果使用过 SQL Server 在登录时会提供两种登录方式: Windows 身份验证 或 SQL Server 身份验证。在我们构建自己的应用的时候同样可以提供 Windows 身份验证 这种验证方式,实际上这种验证方式的代码编写并不困难。
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain,
string lpszPassword, int dwLogonType,
int dwLogonProvider, out int phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CloseHandle(IntPtr phToken);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
string username = USERNAME_TEXTBOX.Text;//USERNAME_TEXTBOX 是你的用户名输入框的name值
string password = PASSWORDBOX.Password;
string Domain = System.Environment.UserDomainName;
int token;
if (LogonUser(name, Domain, password, 2, 0, out token)) {
CloseHandle((IntPtr)token);
Login();
}
else
MessageBox.Show("登录失败, 错误代码:" + GetLastError());
}
Tips:这只是示例,你还需要将以上代码进行合理封装。例如你可以新建一个 AppSecurity 项目,其中提供多种验证方式,同时这个项目暴露一个容易使用的 API 供用户层(也就是你的WPF、Winform等工程)使用,这样你可以将用户层代码和底层实现分开,之后的工程维护可以轻松不少,替换用户层实现或者底层实现变得更加容易
参考列表
- C# 调用 windows 系统内部的身份验证方法
- C# 调用 windows 本身的 logonUser 方法
- 使用 Win32API LogonUser 在 C# 程序中进行域认证
- 处理 Windows 不允许 LogonUser 以空的密码登录问题
- 获得本机计算机名字,本机当前系统登陆用户和判断管理员权限
- WinApi - GetLastError vs. Marshal.GetLastWin32Error
- Windows 错误码大全
- 【Microsoft】LogonUserA function
出现了!STA
Application.Current.Dispatcher.Invoke(()=> {
//操作界面控件
});
使用算法加密传输数据
顺水推舟——拦截消息并自定义处理
进一步解耦:MSMQ
利用windows性能监视器监视性能
在WPF中使用WinForm控件
首先需要引入 WindowsFormsIntegration.dll 和 System.Windows.Forms.dll 两个 Dll。引用-右键-添加引用-搜索对应的 dll 名称即可,并在主界面 xaml 中添加以下内容
xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
xmlns:wf ="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
在 WPF 中使用 Winform 控件首先要添加 WinForm 控件的宿主容器 WindowsFormsHost,用于衔接 WPF 和 WinForm。
在 xaml 中使用 Winform 控件(利用 WindowsFormsHost)。
<wfi:WindowsFormsHost x:Name="winFormsHost" Grid.Row="0" Grid.Column="1" Width="Auto">
<wf:PictureBox x:Name="WinformPictureBox"/>
</wfi:WindowsFormsHost>
c# 代码中使用 Winform PictureBox 控件,注意的是不要导入 Winform 控件的命名空间会和 WPF 的控件命名空间冲突。
System.Windows.Forms.PictureBox image = new System.Windows.Forms.PictureBox
{
Image = System.Drawing.Image.FromFile(@"IMAGE_PATH"),
SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage
};
WindowsFormsHost windowsFormsContainer = new WindowsFormsHost
{
Child = image
};
windowsFormsContainer.SetValue(Grid.ColumnProperty, 1);
windowsFormsContainer.SetValue(Grid.RowProperty, 1);
WPF_GRID.Children.Add(windowsFormsContainer);
参考了 【CSDN】在 WPF 中使用 WinForm 控件方法 和 【CSDN】C# 下 WPF 中调用 WinForm 控件
升级 .NET 版本出现错误
你的项目中有N个模块,因为某种需要你需要升级某个模块.NET版本(例如从.NET 4.5 升级为 .NET 4.6.1),但是升级之后发现编译错误,错误信息大意为——升级之后导致项目版本不一致问题:该框架版本高于当前目标框架“.NETFramework,Version=v4.5”
解决方案是首先清理该模块之前编译的版本,之后查找是否有引用该模块的其他部分,这些部分的 .NET 版本同样需要更新。之后整体工程重新编译即可。