本文记录了笔者在阅读 WPF 程序过程中不懂的知识点和一些心得、理解,同时也是一种高效的学习方式的探索。
56. 使用 notepad 读取文本文件
private void ShowRunningLog()
{
var fileName = "ProductionLine.txt";
if (File.Exists(fileName))
{
Process.Start("notepad.exe", fileName);
}
else
{
MessageBox.Show("日志文件不存在");
}
}
private void ShowReaderConfig()
{
var fileName = "ReaderOptions.json";
if (File.Exists(fileName))
{
Process.Start("notepad.exe", fileName).WaitForExit();
}
else
{
MessageBox.Show("扫码枪配置文件不存在");
}
}
57. 列表信息超过 100 条的时候移除前 50 条
if (Messages.Count > 100)
{
var count = 0;
while (count++ < 50)
{
Messages.RemoveAt(0);
}
}
58. 多线程共用属性的锁线程修改
private AutoResetEvent signal = new AutoResetEvent(true);
[BindingProperty(Target = "newMessage")]
public string NewMessage
{
get => newMessage;
set
{
signal.WaitOne();
newMessage = value;
AsyncUIAction(() =>
{
Messages.Add($"{DateTime.Now:G} {newMessage}");
RaisedPropertyChanged(nameof(Messages));
});
signal.Set();
}
}
...
public virtual void AsyncUIAction(Action action)
{
System.Windows.Application.Current?.Dispatcher.Invoke(action);
}
59. 字符串空值检测 string.IsNullOrEmpty(WriteValue)
if (string.IsNullOrEmpty(WriteValue))
{
return;
}
60. 相同赋值不触发
string frontShell;
public string FrontShell
{
get => frontShell;
set
{
if (value == frontShell)
{
return;
}
frontShell = value;
RaisedPropertyChanged();
}
}
61. 将每种标签的样式单独创建一个文件保存:Styles/Button.xaml
62. 引入外部程序集并声明需要使用的函数签名
[DllImport("VisionAlgorithmCheckTarget.dll", CharSet = CharSet.Ansi)]
static extern int TargetCheck(IntPtr imgData,int w,int h, IntPtr jsonParam, IntPtr jsonOut);
63. 文件对话框
private void Button_Click(object sender, RoutedEventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "D:\\"; // 设置初始目录
openFileDialog.Filter = "图片文件(*.jpg;*.jpeg;*.gif;*.bmp;*.png)|*.jpg;*.jpeg;*.gif;*.bmp;*.png"; // 设置文件过滤器
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// 获取选定的文件路径
Console.WriteLine(openFileDialog.FileName);
}
}
}
64. 获取某个范围之内的随机数
var rand = new Random();
rand.Next(allImgs.Count)
上述代码用来随机获取一张图片集中的图片。
65. 只读的 checkbox 并在输入正确密码之后自动选中
先构建 UI 界面,包括一个标签、一个密码输入框和一个登录按钮。
<StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" >
<CheckBox Content="工程师" HorizontalAlignment="Center" Foreground="WhiteSmoke" VerticalAlignment="Center" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" FontSize="20" IsEnabled="False">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Foreground" Value="DarkBlue"/>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEngineer,UpdateSourceTrigger=PropertyChanged}" Value="false">
<Setter Property="IsChecked" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsEngineer,UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="IsChecked" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<StackPanel Orientation="Vertical" Margin="0,5" >
<Label Content="密码" FontSize="30" FontWeight="Bold" BorderThickness="0" Background="Transparent" Foreground="WhiteSmoke"/>
<PasswordBox x:Name="PWD1" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" FontSize="15" Width="200" Height="30" Margin="2"/>
</StackPanel>
<Button Margin="5 0" Height="40" Width="100" FontSize="20" Content="登录" Click="Button_Click" Background="{DynamicResource ButtonBackgroundColor8}"/>
</StackPanel>
下面是上述 UI 代码的解释:
主体结构
<StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center">
- 创建一个
StackPanel
布局容器。 Grid.Row="1" Grid.Column="1"
:指定这个StackPanel
位于父容器中的Grid
布局的第 1 行第 1 列。HorizontalAlignment="Center"
:将StackPanel
水平居中对齐。
第一部分:工程师复选框
<CheckBox Content="工程师" HorizontalAlignment="Center" Foreground="WhiteSmoke" VerticalAlignment="Center" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" FontSize="20" IsEnabled="False">
- 创建一个
CheckBox
控件。 Content="工程师"
:显示的文本内容为“工程师”。HorizontalAlignment="Center"
:将复选框水平居中对齐。Foreground="WhiteSmoke"
:字体颜色设置为浅灰色(WhiteSmoke
)。VerticalAlignment="Center"
:将复选框垂直居中对齐。HorizontalContentAlignment="Left"
:复选框内容(文本)水平左对齐。VerticalContentAlignment="Center"
:复选框内容垂直居中对齐。FontSize="20"
:字体大小为 20。IsEnabled="False"
:复选框默认禁用(不可点击)。
<CheckBox.Style>
<Style TargetType="CheckBox">
- 定义一个针对
CheckBox
的样式。
<Setter Property="Foreground" Value="DarkBlue"/>
- 设置
CheckBox
的字体颜色为深蓝色(DarkBlue
),注意这里的属性值会覆盖前面设定的Foreground="WhiteSmoke"
。
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
</Setter.Value>
</Setter>
- 使用
LayoutTransform
对CheckBox
进行二维变换。 ScaleTransform ScaleX="1.5" ScaleY="1.5"
:将CheckBox
控件放大 1.5 倍。
<Style.Triggers>
<DataTrigger Binding="{Binding IsEngineer,UpdateSourceTrigger=PropertyChanged}" Value="false">
<Setter Property="IsChecked" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsEngineer,UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="IsChecked" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
- 定义数据触发器,根据
IsEngineer
属性的值来设置CheckBox
的IsChecked
属性。 Binding="{Binding IsEngineer,UpdateSourceTrigger=PropertyChanged}"
:绑定到IsEngineer
属性,UpdateSourceTrigger=PropertyChanged
表示在属性值变化时立即更新。Value="false"
和Value="true"
:分别表示IsEngineer
属性为假和真时的触发逻辑。Setter Property="IsChecked"
:设置CheckBox
的勾选状态。
第二部分:密码输入框
<StackPanel Orientation="Vertical" Margin="0,5">
- 创建一个竖向排列的
StackPanel
。 Orientation="Vertical"
:子元素垂直排列。Margin="0,5"
:设置外边距为 0(上下)和 5(左右)。
<Label Content="密码" FontSize="30" FontWeight="Bold" BorderThickness="0" Background="Transparent" Foreground="WhiteSmoke"/>
- 创建一个
Label
控件。 Content="密码"
:显示的文本内容为“密码”。FontSize="30"
:字体大小为 30。FontWeight="Bold"
:字体加粗。BorderThickness="0"
:边框厚度为 0。Background="Transparent"
:背景颜色为透明。Foreground="WhiteSmoke"
:字体颜色为浅灰色。
<PasswordBox x:Name="PWD1" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" FontSize="15" Width="200" Height="30" Margin="2"/>
- 创建一个
PasswordBox
控件(密码输入框)。 x:Name="PWD1"
:设置控件名称为PWD1
。HorizontalContentAlignment="Left"
:输入内容水平左对齐。VerticalContentAlignment="Center"
:输入内容垂直居中对齐。FontSize="15"
:字体大小为 15。Width="200"
和Height="30"
:设置宽度为 200,高度为 30。Margin="2"
:设置外边距为 2。
第三部分:登录按钮
<Button Margin="5 0" Height="40" Width="100" FontSize="20" Content="登录" Click="Button_Click" Background="{DynamicResource ButtonBackgroundColor8}"/>
- 创建一个
Button
控件。 Margin="5 0"
:设置外边距为 5(上下)和 0(左右)。Height="40"
和Width="100"
:设置按钮的高度为 40,宽度为 100。FontSize="20"
:字体大小为 20。Content="登录"
:按钮上的文本为“登录”。Click="Button_Click"
:点击按钮时调用的事件处理方法为Button_Click
。Background="{DynamicResource ButtonBackgroundColor8}"
:按钮的背景颜色绑定到动态资源ButtonBackgroundColor8
。
总结
这段代码定义了一个包含以下元素的用户界面布局:
- 一个禁用的“工程师”复选框。
- 一个带“密码”标签和密码输入框的容器。
- 一个带有点击事件的登录按钮。
然后是点击登录按钮之后的回调:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (PWD1.Password == "***")
{
var loginModel = ApplicationInfo.ServiceProvider.GetViewModel<Home_VisionProjectViewModel>();
loginModel.IsEngineer = true;
MessageBoxX.Show("工程师身份登录成功!");
PWD1.Password = "";
}
else
MessageBoxX.Show("密码输入错误,请重试!");
}
66. 定义扩展名数组
string[] imageExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".ico" };
string[] txtExtensions = { ".txt" };
67. 手动拼接路径
string ClassFile = LabelPath + "\\classes.txt";
68. System.IO 中的一些常用的方法
以下是对于你提到的五个方法的逐条解释:
1. System.IO.Path.GetFullPath(string path)
-
功能:获取指定路径的绝对路径。
-
参数:
path
:表示路径的字符串,可以是相对路径或绝对路径。
-
返回值:
- 返回一个字符串,表示指定路径的完整绝对路径。路径会被规范化,例如,多余的分隔符会被移除,相对路径(如
..
或.
)会被解析为绝对路径。
- 返回一个字符串,表示指定路径的完整绝对路径。路径会被规范化,例如,多余的分隔符会被移除,相对路径(如
-
用途:
-
在需要获取文件或目录的绝对路径时使用,确保路径的合法性。
-
示例:
string relativePath = "../folder/file.txt"; string fullPath = Path.GetFullPath(relativePath); Console.WriteLine(fullPath); // 输出当前工作目录的完整路径,例如:"C:\Users\username\folder\file.txt"
-
2. System.IO.Directory.Exists(string path)
-
功能:检查指定的目录是否存在。
-
参数:
path
:表示目录路径的字符串。
-
返回值:
- 返回一个布尔值,如果目录存在返回
true
,否则返回false
。
- 返回一个布尔值,如果目录存在返回
-
用途:
-
在执行文件或目录操作之前,先检查目录是否存在,以避免不必要的错误。
-
示例:
string directoryPath = @"C:\Users\username\Desktop\MyFolder"; bool exists = Directory.Exists(directoryPath); Console.WriteLine(exists); // 如果目录存在,输出 "True",否则 "False"
-
3. System.IO.Directory.GetFiles(string path)
-
功能:获取指定目录中的文件列表。
-
参数:
path
:表示目录路径的字符串。
-
返回值:
- 返回一个字符串数组,包含指定目录中的所有文件的绝对路径。如果目录为空或不存在,则返回空数组。
-
用途:
-
需要获取目录中的文件列表时使用,例如批量处理文件。
-
示例:
string directoryPath = @"C:\Users\username\Desktop\Documents"; string[] files = Directory.GetFiles(directoryPath); foreach (string file in files) { Console.WriteLine(file); // 输出每个文件的路径 }
-
4. System.IO.Path.GetExtension(string path)
-
功能:获取文件的扩展名。
-
参数:
path
:表示文件路径的字符串。
-
返回值:
- 返回一个字符串,表示文件的扩展名(包含前面的点,例如
.txt
或.jpg
)。如果文件没有扩展名或路径不合法,则返回空字符串。
- 返回一个字符串,表示文件的扩展名(包含前面的点,例如
-
用途:
-
在需要判断文件类型或根据扩展名进行操作时使用。
-
示例:
string filePath = "document.txt"; string extension = Path.GetExtension(filePath); Console.WriteLine(extension); // 输出 ".txt"
-
5. System.IO.File.ReadAllText(string path)
-
功能:读取文件的全部内容。
-
参数:
path
:表示文件路径的字符串。
-
返回值:
- 返回一个字符串,包含文件的全部内容。
-
用途:
-
当需要快速读取整个文本文件的内容时使用,支持文本编码自动检测。
-
示例:
string filePath = "info.txt"; string content = File.ReadAllText(filePath); Console.WriteLine(content); // 输出文件的全部内容
-
这些方法是 .NET 中处理文件和目录操作时非常常用的工具,能够帮助开发者更高效地进行文件相关的开发工作。
69. 做一个表格页
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1.1*"/>
<ColumnDefinition Width="1.1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="开始日期" VerticalAlignment="Center" FontSize="25" Foreground="White"/>
<hc:DatePicker Grid.Column="1" x:Name="startTime" ShowClearButton="False" Width="120" Height="40" Margin="10,0,30,0"/>
<TextBlock Grid.Column="2" Text="结束日期" VerticalAlignment="Center" FontSize="25" Foreground="White"/>
<hc:DatePicker Grid.Column="3" x:Name="endTime" ShowClearButton="False" Width="120" Height="40" Margin="10,0,30,0"/>
<TextBlock Text="序列号" Grid.Column="4" VerticalAlignment="Center" FontSize="25" Foreground="White"/>
<TextBox Text="{Binding SNKey}" Grid.Column="5" Width="120" Height="40" Margin="10,0,30,0"/>
<TextBlock Text="订单号" Grid.Column="6" VerticalAlignment="Center" FontSize="25" Foreground="White"/>
<TextBox Text="{Binding OrderKey}" Grid.Column="7" Width="120" Height="40" Margin="10,0,30,0"/>
<Button Grid.Column="8" Content="查询" Foreground="White" Margin="10 0 10 0" FontSize="20" Width="150" Height="85" Click="Button_Click_1" Background="{DynamicResource ButtonBackgroundColor}" />
<Button Grid.Column="9" Content="导出" Foreground="White" Margin="10 0 10 0" FontSize="20" Width="150" Height="85" Click="Button_Click" Background="{DynamicResource ButtonBackgroundColor}" />
</Grid>
Page>Grid>(Grid.Resources>ResourceDictionary>ResourceDictionary.MergedDictionaries>ResourceDictionary*2)+(TabControl>TabItem>Grid>(Grid.RowDefinations>RowDefination*4)+(Grid>Grid.ColumnDefinations>ColumnDefination*10))
表格的内容页
Grid>DataGrid>DataGrid.Columns[ItemSource={}]>DataGridTextColumn[Binding={Binding .} Header="***" Width=***]*8
70. 文本转时间格式
DateTime T1 = Convert.ToDateTime(startTime.Text);
DateTime T2 = Convert.ToDateTime(endTime.Text).AddDays(1); //默认结束加1天
71. 使用 ISqlSugarClient 查询数据并输出到指定变量中去
使用的 API 名称主要有 where 和 ToList.
// 获取注入的服务单例
var ctx = ApplicationInfoBase.ServiceProvider.GetService<ISqlSugarClient>()
// 筛选数据
var resultitem = ctx.Queryable<TestingRecord>().Where(g => g.TestTime <= T2 && g.TestTime >= T1 && g.SN == home_VisionRecordPageViewModel.SNKey && g.OrderCode == home_VisionRecordPageViewModel.OrderKey).ToList();
// 将筛选得到的数据输出
RecordData.ItemsSource = resultitem;
72. 将 DataGrid
中的数据输出成 Excel 文件
以下是将 DataGrid
中的数据输出成 Excel 文件的代码拆解及说明:
代码拆解
1. 检查数据是否为空
csharp复制
if (RecordData.Items.Count == 0)
{
MessageBoxX.Show("未查询到要导出的数据,请重试!");
return;
}
-
作用:检查
DataGrid
中是否有数据。 -
解释:
RecordData.Items.Count
返回DataGrid
中的项数。- 如果没有数据,弹出提示框,提示用户“未查询到要导出的数据”,并终止后续操作。
2. 选择所有单元格
csharp复制
RecordData.SelectAllCells();
-
作用:选中
DataGrid
中的所有单元格。 -
解释:
SelectAllCells()
是DataGrid
的方法,用于选中所有可见的单元格。
3. 设置剪贴板复制模式
csharp复制
RecordData.ClipboardCopyMode = System.Windows.Controls.DataGridClipboardCopyMode.IncludeHeader;
-
作用:设置
DataGrid
的剪贴板复制模式为包含列头。 -
解释:
ClipboardCopyMode
是DataGrid
的一个属性,指定在复制到剪贴板时是否包含列头。IncludeHeader
表示复制内容时包含列头。
4. 执行复制操作
csharp复制
ApplicationCommands.Copy.Execute(null, RecordData);
-
作用:将
DataGrid
中选中的内容复制到剪贴板中。 -
解释:
ApplicationCommands.Copy
是执行复制操作的命令。
5. 获取剪贴板中的数据
csharp复制
string result = (string)Clipboard.GetData(DataFormats.Text);
-
作用:从剪贴板中获取复制的数据。
-
解释:
Clipboard.GetData(DataFormats.Text)
获取剪贴板中的文本格式数据。
6. 打开文件保存对话框
csharp复制
SaveFileDialog sfd = new SaveFileDialog();
sfd.FileName = "测试记录";
sfd.Filter = "Excel文件(*.xls)|*.xls|Csc文件(*.csv)|*.csv|所有文件(*.*)|*.*";
-
作用:允许用户选择保存位置和文件类型。
-
解释:
SaveFileDialog
是一个文件保存对话框,用于用户选择保存位置。FileName
设置默认的文件名。Filter
设置文件类型过滤器,支持 Excel 文件、Csc 文件和所有文件。
7. 处理文件保存
csharp复制
if (sfd.ShowDialog() == DialogResult.OK)
{
string path = System.IO.Path.GetDirectoryName(sfd.FileName);
RecordData.UnselectAllCells();
StreamWriter swr = new StreamWriter(sfd.FileName, false, Encoding.GetEncoding("gb2312"));
swr.WriteLine(result.Replace(',', ' '));
swr.Flush();
swr.Close();
swr.Dispose();
MessageBoxX.Show($"数据导出成功!");
}
-
作用:将数据写入到用户选择的文件中。
-
解释:
-
ShowDialog()
显示文件保存对话框,等待用户选择。 -
如果用户点击了“确定”,继续执行以下操作:
UnselectAllCells()
取消选中DataGrid
中的所有单元格。StreamWriter
是用于写入文件的类。Encoding.GetEncoding("gb2312")
指定文件使用的编码。swr.WriteLine(result.Replace(',', ' '))
将剪贴板中的数据写入文件。Flush()
和Close()
确保数据成功写入并关闭文件。- 弹出提示框,提示用户“数据导出成功”。
-
8. 异常处理
csharp复制
catch (Exception ex)
{
MessageBoxX.Show($"数据导出失败,具体原因{ex}");
}
-
作用:捕获并处理导出过程中可能发生的异常。
-
解释:
- 如果导出过程中发生异常,会捕获异常并弹出提示框,显示异常信息。
工作原理总结
- 首先检查
DataGrid
中是否有数据。 - 选中
DataGrid
中的所有单元格,并设置复制模式为包含列头。 - 将选中的内容复制到剪贴板。
- 弹出文件保存对话框,用户选择保存位置和文件类型。
- 将剪贴板中的数据写入到指定文件中。
- 使用
StreamWriter
将数据写入到文件中。 - 如果文件写入成功,弹出提示框,提示用户数据导出成功;如果失败,捕获异常并提示用户。
注意事项
DataGrid
的列头必须可见,因为ClipboardCopyMode
设置为IncludeHeader
,否则列头不会被复制。- 如果导出的数据包含特殊字符(如逗号、换行符等),可能需要进一步处理。
- 在实际应用中,可以考虑将编码设置为
Encoding.UTF8
或其他编码,以支持更多字符集。 - 如果需要更多的格式化(如合并单元格、设置样式等),可以使用第三方库(如 EPPlus 或 ClosedXML)来生成 Excel 文件。
全部代码
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
if (RecordData.Items.Count == 0)
{
MessageBoxX.Show("未查询要导出的数据,请重试!");
return;
}
RecordData.SelectAllCells();
RecordData.ClipboardCopyMode = System.Windows.Controls.DataGridClipboardCopyMode.IncludeHeader;
ApplicationCommands.Copy.Execute(null, RecordData);
string result = (string)Clipboard.GetData(DataFormats.Text);
SaveFileDialog sfd = new SaveFileDialog();
sfd.FileName = "测试记录";
sfd.Filter = "Excel文件(*.xls)|*.xls|Csc文件(*.csv)|*.csv|所有文件(*.*)|*.*"; ;
if (sfd.ShowDialog() == DialogResult.OK)
{
string path = System.IO.Path.GetDirectoryName(sfd.FileName);
RecordData.UnselectAllCells();
StreamWriter swr = new StreamWriter(sfd.FileName, false, Encoding.GetEncoding("gb2312"));
//StreamWriter swr = new StreamWriter(sfd.FileName, false, System.Text.Encoding.GetEncoding(-0));
swr.WriteLine(result.Replace(',', ' '));
swr.Flush();
swr.Close();
swr.Dispose();
MessageBoxX.Show($"数据导出成功!");
}
}
catch (Exception ex)
{
MessageBoxX.Show($"数据导出失败,具体原因{ex}");
}
}
73. 构建一个带图标的按钮
全部代码如下
<RadioButton IsChecked="True"
Content="关于"
Style="{DynamicResource MenuRadioButtonStyle2}"
Tag="{DynamicResource report}" Margin="0 5 0 5"
Command="{Binding ShowPageCommand}"
CommandParameter="/Views/Pages/Help/Help_AboutPage.xaml"/>
---
<Style x:Key="MenuRadioButtonStyle2" TargetType="{x:Type RadioButton}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryForegroundColor}"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="36"/>
<Setter Property="Height" Value="60"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Border x:Name="menuButton" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- Selected -->
<Border x:Name="btnSelected" Grid.ColumnSpan="2" CornerRadius="4" />
<!-- Indicator -->
<!--
<Rectangle Name="Indicator" Grid.ColumnSpan="2" HorizontalAlignment="Left" Width="4" Height="40" VerticalAlignment="Center" RadiusX="2" RadiusY="2"/>-->
<!-- Icon -->
<Path x:Name="Icon" Data="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Height="30" Width="30" Stretch="None" Fill="{DynamicResource PrimaryForegroundColor}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="20 0 2 0"/>
<!-- Text -->
<TextBlock x:Name="txtName" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10 0 10 0"
Grid.Column="1" Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}"
FontWeight="{TemplateBinding FontWeight}"
FontSize="{TemplateBinding FontSize}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="txtName" Property="Foreground" Value="{DynamicResource MenuForegroundColor}"/>
<Setter TargetName="Icon" Property="Fill" Value="{DynamicResource MenuForegroundColor}"/>
<!-- Optional
<Setter TargetName="Indicator" Property="Fill" Value="red"/>
<Setter TargetName="Indicator" Property="Height" Value="20"/>-->
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Icon" Property="Fill" Value="{DynamicResource MenuForegroundColor}"/>
<Setter TargetName="btnSelected" Property="Background" Value="{DynamicResource BackgroundColor3}"/>
<Setter TargetName="txtName" Property="Foreground" Value="{DynamicResource MenuForegroundColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
74. 点击按钮切换视图
首先使用 Frame 组件创建一个视图窗口
<Frame x:Name="PagesNavigation" NavigationUIVisibility="Hidden" Source="{Binding ChildPageUri}">
然后将改变 ChildPageUri 这个属性的回调函数绑定在按钮命令上即可:
<RadioButton IsChecked="True"
Content="按钮名称"
Style="{DynamicResource MenuRadioButtonStyle2}"
Tag="{DynamicResource report}" Margin="0 5 0 5"
Command="{Binding ShowPageCommand}"
CommandParameter="/Views/Pages/*/**.xaml"/>
ChildPageUri 属性的定义及按钮的回调函数
private string childPageUri = "/Views/Pages/*/**.xaml";
public string ChildPageUri
{
get { return childPageUri; }
set { childPageUri = value; RaisedPropertyChanged(); }
}
public ICommand ShowPageCommand => new RelayCommand<string>(ShowPage);
private void ShowPage(string uri)
{
ChildPageUri = uri;
}
75. Frame 组件
<Frame Grid.Column="1" Source="{Binding ChildPageUri}" NavigationUIVisibility="Hidden"></Frame>
1. Frame
组件
Frame
是 WPF 中用于导航的容器控件,用于在应用程序中加载和显示页面(Page
)。- 它类似于网页中的框架,可以将页面加载到
Frame
中,并在Frame
中导航。
2. Grid.Column="1"
Grid.Column
是Grid
布局中的重要属性,用于指定控件所在的列。- 这里的
Grid.Column="1"
表示该Frame
被放置在Grid
的第 1 列(列索引从 0 开始)。 Grid
是一种常见的布局控件,通过定义行(RowDefinitions
)和列(ColumnDefinitions
)来组织 UI 元素。
3. Source="{Binding ChildPageUri}"
Source
属性指定Frame
要加载的页面的 URI。"{Binding ChildPageUri}"
是数据绑定语法,表示Source
属性绑定到数据上下文中的ChildPageUri
属性。- 这意味着
ChildPageUri
的值可以动态变化,当ChildPageUri
的值改变时,Frame
会加载新的页面。
4. NavigationUIVisibility="Hidden"
NavigationUIVisibility
控制Frame
是否显示导航 UI(如“后退”和“前进”按钮)。- 其值为
"Hidden"
表示隐藏导航 UI,这意味着用户无法通过Frame
的内置控件进行导航,开发者需要手动实现导航逻辑。
综合解释
这段代码定义了一个 Frame
组件,其主要功能和特性如下:
- 布局位置:通过
Grid.Column="1"
指定Frame
位于Grid
的第 1 列。 - 页面加载:使用
Source="{Binding ChildPageUri}"
绑定到数据上下文中的ChildPageUri
属性,动态加载指定的页面。 - 隐藏导航 UI:通过
NavigationUIVisibility="Hidden"
隐藏了Frame
的默认导航按钮,需要通过其他方式实现页面导航逻辑。
示例场景
假设有一个主页面(MainPage
),其中包含一个 Grid
和一个 Frame
,开发者希望通过导航到不同的页面来实现功能切换。以下是一个简单的示例:
MainPage.xaml
<Page x:Class="MyApp.MainPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 导航按钮 -->
<Button Grid.Row="0" Content="Go to Page1" Click="GoToPage1_Click"/>
<Button Grid.Row="0" Content="Go to Page2" Click="GoToPage2_Click"/>
<!-- Frame 用于加载页面 -->
<Frame Grid.Row="1" Source="{Binding CurrentPageUri}" NavigationUIVisibility="Hidden"/>
</Grid>
</Page>
MainPage.xaml.cs
public partial class MainPage : Page, INotifyPropertyChanged
{
private string _currentPageUri;
public string CurrentPageUri
{
get { return _currentPageUri; }
set
{
_currentPageUri = value;
OnPropertyChanged();
}
}
public MainPage()
{
InitializeComponent();
DataContext = this;
}
private void GoToPage1_Click(object sender, RoutedEventArgs e)
{
CurrentPageUri = "Page1.xaml";
}
private void GoToPage2_Click(object sender, RoutedEventArgs e)
{
CurrentPageUri = "Page2.xaml";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
运行效果
- 点击“Go to Page1”按钮时,
CurrentPageUri
被设置为Page1.xaml
,Frame
会加载Page1.xaml
页面。 - 点击“Go to Page2”按钮时,
CurrentPageUri
被设置为Page2.xaml
,Frame
会加载Page2.xaml
页面。 Frame
的导航 UI 被隐藏,用户无法通过Frame
的内置按钮进行导航,而是通过按钮点击事件实现自定义导航。