|
uwp
在使用 Win32 / WPF / Windows Forms 的打开或保存文件对话框的时候,多数情况下我们都会考虑编写文件过滤器。UWP 中有 FileTypeFilter
集合可以添加不同的文件种类,但 Win32 中却是一个按一定规则组合而成的字符串。
因为其包含一定的格式,所以可能写错。本文介绍如何编写 Filter。
Filter 使用竖线分隔不同种类的过滤器,比如 图片|*.png;*.jpg|文本|*.txt|walterlv 的自定义格式|*.lvyi
。
var dialog = new OpenFileDialog();
dialog.Filter = "图片|*.png;*.jpg|文本|*.txt|walterlv 的自定义格式|*.lvyi";
dialog.ShowDialog(this);
有时我们会看到一些程序的过滤器里面显示了过滤器本身,而不止是名称,实际上是因为名称中包含了过滤器:
图片 (png, jpg)|*.png;*.jpg|文本 (txt)|*.txt|walterlv 的自定义格式 (lvyi)|*.lvyi
你不可以在过滤器中省略名称或者过滤器任何一个部分,否则会抛出异常。
对于 .NET Core 版本的 WPF 或者 Windows Forms 程序来说,需要安装 Windows 兼容 NuGet 包:
安装后可以使用 Windows Forms 版本的 OpenFileDialog
或者 WPF 版本的 Microsoft.Win32.OpenFileDialog
。
参考资料
本文介绍不那么常见的 XAML 相关的知识。
当你用设计器修改元素的 Margin 时,你会看到用逗号分隔的 Thickness
属性。使用设计器或者属性面板时,使用逗号是默认的行为。
不过你有试过,使用空格分隔吗?
<Button Margin="10 12 0 0" />
,
)设置多值枚举有一些枚举标记了 [Flags]
特性,这样的枚举可以通过位运算设置多个值。
[Flags]
enum NonClientFrameEdges
{
// 省略枚举内的值。
}
那么在 XAML 里面如何设置多个枚举值呢?使用逗号(,
)即可,如下面的例子:
<WindowChrome NonClientFrameEdges="Left,Bottom,Right" GlassFrameThickness="0 64 0 0" UseAeroCaptionButtons="False" />
+
)设置多值枚举使用逗号(,
) 设置多值枚举是通用的写法,但是在 WPF/UWP 中设置按键/键盘快捷键的时候又有加号(+
)的写法。如下面的例子:
<KeyBinding Command="{x:Static WalterlvCommands.Foo}" Modifiers="Control+Shift" Key="W" />
这里的 Modifiers
属性的类型是 ModifierKeys
,实际上是因为这个类型特殊地编写了一个 TypeConverter
来转换字符串,所以键盘快捷键多值枚举使用的位或运算用的是加号(+
)。
WPF/UWP 中原生控件的 XAML 命名空间是 http://schemas.microsoft.com/winfx/2006/xaml/presentation,与 XAML 编译器相关的 XAML 命名空间是 http://schemas.microsoft.com/winfx/2006/xaml,还有其他 Url 形式的 XAML 命名空间。
只需要在库中写如下特性(Attribute)即可将命名空间指定为一个 url:
using System.Windows.Markup;
[assembly: XmlnsDefinition("http://walterlv.github.io/demo", "Walterlv.NewCsprojDemo")]
详情请阅读博客:
此写法要生效,定义的组件与使用的组件不能在同一程序集。
WPF/UWP XAML 编译器的命名空间前缀是 x
。如果你写了自己的控件,希望给控件指定一个默认的命名空间前缀,那么可以通过在库中写如下特性(Attribute)实现:
using System.Windows.Markup;
[assembly: XmlnsPrefix("http://walterlv.github.io/demo", "w")]
这样,当 XAML 设计器帮助你自动添加命名空间时,将会使用 w
前缀。虽然实际上你也能随便改。
详情请阅读博客:
此写法要生效,定义的组件与使用的组件不能在同一程序集。
自己写了一个 DemoPage
,要在 XAML 中使用,一般需要添加命名空间前缀才可以。但是也可以不写:
<UserControl
x:Class="HuyaHearhira.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<DemoPage />
</Grid>
</UserControl>
方法是在库中定义命名空间前缀为 http://schemas.microsoft.com/winfx/2006/xaml/presentation。
using System.Windows.Markup;
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Walterlv.NewCsprojDemo")]
此写法要生效,定义的组件与使用的组件不能在同一程序集。
每个人都拥有 好奇心,好奇心驱使着我们总是去尝试做一些有趣的事情。
带起你的好奇心,本文将使用 C# 开发各种各样好玩的东西。
本文内容已加入 2019 年 4 月 13 日的广州 .NET 俱乐部第 2 届线下沙龙。
每个人都拥有 好奇心,好奇心驱使着我们总是去尝试做一些有趣的事情。
比如这件事:
在好奇心的驱使下,我们立刻 尝试 我们的想法。
我们需要用电脑打字,手机端出字;于是我们需要开发的是一款云输入法。而一个最简单的云驱动的软件需要至少一个 Web 后端、一个桌面端和一个移动端。
还没开始呢,就这么复杂。
摆在我们面前的,有两条路可以选:
如果先搞起来,那么我们能够迅速出效果,出产品,出玩具,那么这种成就感会鼓励我们继续完善我们的代码,继续去做更多好玩的东西。
而如果是先掌握所有理论知识再实践,这是我们从学校带来的学习方式,我们中的多数人在校期间就是这么学习的。虽然对学霸来说可以无视,但对于我们这样大多数的小伙伴来说,简直就是“从入门到放弃”。
如果先搞起来呢?如果我们连“入门”都不需要呢?是不是就不需要放弃了!
怎么才能够先搞起来?我们需要调整一下心态——我们不是在学,而是在玩!
我们需要做的是降低学习成本,甚至入门不学习,那么立刻就能玩起来!
我们有 C#,还有什么不能马上搞起来!
打开 Visual Studio 2019,我们先搞起来!
对于简单的云服务来说,使用 Asp.NET Core 开发是非常简单快速的。你可以阅读林德熙的博客入门 Asp.NET Core 开发:
我们是要玩的呀,什么东西好玩。我们自己就是用户,用户看得到的部分才是最具有可玩性的。这就是指客户端或者 Web 前端。
我们现在要拿 C# 写客户端,一般 C# 或者 .NET 的开发者拿什么来写桌面客户端呢?
我们现在已经有至少两个端了。由于我们是同一个软件系统,所以实际上非常容易出现公共代码。典型的就是一些数据模型的定义,以及 Web API 的访问代码,还有一些业务需要的其他公共代码等等。
所以,我们最好使用一个新的项目将这些代码整合起来。
我们选用 .NET Standard 项目来存放这些代码,这样可以在各种 .NET 中使用这些库。
由于我们多数的代码都可以放到 .NET Standard 类库中,以确保绝大多数的代码都是平台和框架无关的,所以实际上我们在其他各个端项目中的代码会是很少的。
这个时候,写一个控制台程序来测试我们的项目,控制台程序的部分其实只需要很少的用于控制控制台输入输出的代码,其他多数的代码例如用来访问 Web API 的代码都是不需要放在控制台项目中的,放到 .NET Standard 的类库中编写就可以做到最大程度的共用了。
接下来要完成这个云键盘程序,我们还需要开发一个移动端。使用 Xamarin 可以帮助我们完成这样的任务。
关于使用 Xamarin.Forms 开发一个键盘扩展,可以阅读我的另一篇博客:
于是,我们仅仅使用 C# 还有客户端开发者熟悉的 XAML 就开发出了三个端了。
这三个端中,有两个都是客户端,于是就会存在向用户分发客户端的问题。虽然可以让用户去商店下载,但是提供一个官方下载页面可以让用户在一处地方找到所有端的下载和部署方法。
这需要使用到前端。然而如何使用 C# 代码来编写去前端呢?
使用 CSHTML5!
你可以前往 CSHTML5 的官网 下载 Visual Studio 的插件,这样你就可以在 Visual Studio 中编写 CSHTML5 的代码了,还有设计器的支持。
于是我们使用 XAML + C# 就编写出了各个端了。
如果没有 GUI,那么跨平台将是非常容易的一件事情。例如我们想要在 Mac 电脑上也做一个打字发送的一方,那么一个控制台应用也是能够直接完成的。
不过,这并不是说,我们只能通过控制台来开发桌面端应用。
我们还有:
利用这些平台,我们能开发其他桌面平台的 GUI 客户端。
另外,利用 ML.NET,我们还能用 C# 进行机器学习。可参见:Bean.Hsiang - 博客园。
利用 Roslyn,我们还能用直接做编译器,然后你还有什么不能做的?关于 Roslyn 的入门,可以阅读:从零开始学习 dotnet 编译过程和 Roslyn 源码分析 - walterlv。
还有 IoT。
还有其他……
每个人都拥有 好奇心,好奇心驱使着我们总是去尝试做一些有趣的事情。
使用你熟悉的语言 C#,不需要太多额外的入门,即可玩转你身边各种你需要的技术栈,玩出各种各样你自己期望尝试开发的小东西。
本文介绍透明度叠加算法(Alpha Blending Algorithm),并用 C#/WPF 的代码,以及像素着色器的代码 HLSL 来实现它。
对于算法,我只是搬运工,可以随意搜索到。算法详情请查看:Alpha compositing - Wikipedia。
对于完全不透明的背景和带有透明度的前景,合并算法为:
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
这是红色。然后绿色 g
和蓝色 b
通道进行一样的计算。最终合成图像的透明通道始终设置为 1。
多数 UI 框架对于颜色值的处理都是用一个 byte
赛表单个通道的一个像素。于是计算会采用 0xff 即 255。
for (int i = 0; i + 4 < length; i = i + 4)
{
var backB = background[i];
var backG = background[i + 1];
var backR = background[i + 2];
var foreB = foreground[i];
var foreG = foreground[i + 1];
var foreR = foreground[i + 2];
double alpha = foreground[i + 3];
blue = 0;
output[i] = (foreB * alpha) + (backB * (1.0 - alpha));
output[i + 1] = (foreG * alpha) + (backG * (1.0 - alpha));
output[i + 2] = (foreR * alpha) + (backR * (1.0 - alpha));
output[i + 3] = 1.0;
}
这段代码当然是跑不起来的,因为是下面两篇博客的魔改代码。你需要阅读以下两篇博客了解如何在 WPF 中按像素修改图像,然后应用上面的透明度叠加代码。
话说,一般 UI 框架都自带有透明度叠加,为什么还要自己写一份呢?
当然是因为某些场景下我们无法使用到 UI 框架的透明度叠加特性的时候。例如使用 HLSL 编写像素着色器的一个实现。
下面使用像素着色器的实现是我曾经写过的一个特效的一个小部分,我把透明度叠加的部分单独摘取出来。
以下是 HLSL 代码的实现。Background 是从采样寄存器 0 取到的颜色采样,Foreground 是从采样寄存器 1 取到的颜色采样。
这里的计算中,背景是不带透明度的,而前景是带有透明度的。
/// <description>透明度叠加效果。</description>
sampler2D Background : register(s0);
sampler2D Foreground : register(s1);
float4 main(float2 uv : TEXCOORD) : COlOR
{
float4 background = tex2D(Background, uv);
float4 foreground = tex2D(Foreground, uv);
float alpha = foreground.a;
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
float g = (foreground.g * alpha) + (background.g * (1.0 - alpha));
float b = (foreground.b * alpha) + (background.b * (1.0 - alpha));
float a = 1.0;
return float4(r, g, b, a);
}
如果要测试的图片都是不带透明度的,那么可以通过自己设一个透明度来模拟,传入透明度值 Alpha。
/// <description>透明度叠加效果。</description>
/// <type>Double</type>
/// <summary>采样 2 的叠加透明度。</summary>
/// <minValue>0.0</minValue>
/// <maxValue>1.0</maxValue>
/// <defaultValue>0.75</defaultValue>
float Alpha : register(C0);
sampler2D Background : register(s0);
sampler2D Foreground : register(s1);
float4 main(float2 uv : TEXCOORD) : COlOR
{
float4 background = tex2D(Background, uv);
float4 foreground = tex2D(Foreground, uv);
float alpha = Alpha;
float r = (foreground.r * alpha) + (background.r * (1.0 - alpha));
float g = (foreground.g * alpha) + (background.g * (1.0 - alpha));
float b = (foreground.b * alpha) + (background.b * (1.0 - alpha));
float a = 1.0;
return float4(r, g, b, a);
}
参考资料
如果在开发过程,遇到多个页面之间,需要传输信息,那么可能遇到设计的问题。如果因为一个页面内包含多个子页面和多个子页面之间的通信问题找不到一个好的解决方法,那么请看本文。如果因为ViewModel代码越来越多烦恼,请试试本文提供的框架。
本文介绍我做的框架,这是一个轻量的框架,可以同时使用其它的框架,用于多个页面之间,多个 ViewModel 之间的通信。