2022/1/4
1. 静态图标
将Image嵌入原控件
<Button>
<Image Source="Resources\help.png"/>
</Button>
在模板中加多一个Image控件并指定永久性Source
<Image x:Name="img" Source="Resources\feedbackclose.png"/>
2. 动态图标
在模板中添加Image控件,通过Source设定原始图标
删除原来的IsMouseOver触发器 和 IsPressed触发器
添加Triggers,Proproty=“IsMouseOver”,Value=“True”,重置Image的Source
添加Triggers,Proproty=“IsPressed”,Value=“True”,重置Image的Source
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="img" Property="Source" Value="Resources\morehover.png"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="img" Property="Source" Value="Resources\morepush.png"/>
</Trigger>
3. 弹窗Popup
Popup,
PlacementTarget用于绑定弹在哪个元素,
Placement用于决定绑定在元素的哪个位置,
PlacementStaysOpen用于决定失焦后是否保持可见,
Closed事件发生在IsOpen为false时,
<Popup x:Name="p"
PlacementTarget="{Binding ElementName=b}"
Placement="Bottom"
StaysOpen="False"
Closed="p_Closed" >
4. 文本框提示
Border,
Height为边框高度,
Width为边框宽度,
BorderBrush为边框颜色,
BorderThickness为边框大小,
VisualBrush,
Visutal为放入笔刷的内容,
AlignmentX为笔刷从哪里横坐标开始画,
AlignmentY为笔刷从哪里纵坐标开始画,
Stretch为笔刷内容是否按某种方式拉伸,
TextBox,
Height为边框高度,
Width为边框宽度,
BorderBrush为边框颜色,
BorderThickness为边框粗度,
TextWrapping为内容是否自动换行,
为Textbox添加样式触发器,当Text的Value为空时则使用自定义背景画刷,
另外,删除Textbox模板的触发器IsMouseOver和IsKeyboardFocused
<Border Height="200" Width="200" BorderBrush="black" BorderThickness="1">
<TextBox Template="{DynamicResource TextBoxTemplate1}" Height="200" Width="200" BorderBrush="Transparent" BorderThickness="5" TextWrapping="Wrap">
<TextBox.Resources>
<VisualBrush x:Key="backbrush" AlignmentX="Left" AlignmentY="Top" Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="请输入名称"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Resources>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{StaticResource backbrush}"/>
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource backbrush}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
2022/1/5
1. 学习MVVM示例
2. 样式与控件模板
样式用于定义控件的外形以及属于空间的一些属性触发器,
模板用于一定控件的内容以及内容之间的一些触发器,
样式声明内可包含模板,样式与模板也可以单独声明,
单独Style
<Style x:Key="buttonstyle1" TargetType="Button">
<Setter Property="Background" Value="Pink"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Content" Value="content"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content" Value="555"/>
</Trigger>
</Style.Triggers>
</Style>
单独ControlTemplate
<ControlTemplate x:Key="buttontemplate1" TargetType="Button">
<Border x:Name="border">
<Image x:Name="img" Source="Resources\feedbackclosehover.png" Height="40" Width="40" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Green"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="Yellow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Style中声明ControlTemplate
<Style x:Key="buttonstyle1" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border">
<Image x:Name="img" Source="Resources\feedbackclosehover.png" Height="40" Width="40" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Green"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="Yellow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
3. 数据模板
CellTemplate(自定义模板为表格内容)
<DataGrid x:Name="gd" AutoGenerateColumns="False" CanUserSortColumns="True" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding UserName}" Width="100" Header="学生姓名"/>
<DataGridTextColumn Binding="{Binding ClassName}" Width="100" Header="班级名称"/>
<DataGridTextColumn Binding="{Binding Address}" Width="200" Header="地址"/>
<DataGridTemplateColumn Header="操作" Width="100" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Button Content="编辑"/>
<Button Margin="8 0 0 0" Content="删除" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ItemTemplate(自定义模板为控件项内容)
<Window.Resources>
<DataTemplate x:Key="comTemplate">
<StackPanel Orientation="Horizontal" Margin="5,0">
<Border Width="10" Height="10" Background="{Binding Code}"/>
<TextBlock Text="{Binding Code}" Margin="5,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ComboBox Name="cob" Width="120" Height="30" ItemTemplate="{StaticResource comTemplate}"/>
<ListBox Name="lib" Width="120" Height="100" Margin="5,0" ItemTemplate="{StaticResource comTemplate}"/>
</StackPanel>
</Grid>
ItemsControl(利用数据生成相应的控件)
<ItemsControl Name="ic">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="50" Height="50" Content="{Binding Code}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
2022/1/6
1. 编写WPF入门笔记
2. 学习 MVVM实例
3. 自编写MVVM实例
.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="{Binding UpdateCommand}" Content="刷新"/>
<TextBlock Text="班级名称:" Margin="10,0,0,0"/>
<TextBlock Text="{Binding ClassName}"/>
</StackPanel>
<DataGrid Grid.Row="1" ItemsSource="{Binding Students}" AutoGenerateColumns="False" CanUserAddRows="False" ColumnWidth="*">
<DataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid},Path=SelectedItem}"
Command="{Binding ShowCommand}"/>
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="名称"/>
<DataGridTextColumn Binding="{Binding Age}" Header="年龄"/>
<DataGridTextColumn Binding="{Binding Sex}" Header="性别"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
.xaml.cs
this.DataContext = new ViewModel.MainViewModel();
MainViewMedol.cs
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
//数据初始化
ClassName = "高二三班";
Students = new ObservableCollection<Student>();
Students.Add(new Student() { Name = "张三", Age = 28, Sex = "男" });
Students.Add(new Student() { Name = "李四", Age = 26, Sex = "女" });
//命令声明
ShowCommand = new RelayCommand<Student>(Show);
UpdateCommand = new RelayCommand(Update);
}
//班级名称
private string className;
public string ClassName
{
get { return className; }
set { className = value;RaisePropertyChanged(); }
}
//表格数据
private ObservableCollection<Student> students;
public ObservableCollection<Student> Students
{
get { return students; }
set { students = value; RaisePropertyChanged(); }
}
//show 命令
public RelayCommand<Student> ShowCommand { get; set; }
public void Show(Student stu)
{
MessageBox.Show($"Name:{stu.Name} Age:{stu.Age} Sex:{stu.Sex}");
}
//update classname 命令
public RelayCommand UpdateCommand { get; set; }
public void Update()
{
ClassName = "高三三班";
}
}
Student.cs
public class Student
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
private string sex;
public string Sex
{
get { return sex; }
set { sex = value; }
}
}
DB文件,用于程序有初始化数据时创建
Models文件,对事物进行抽象,并设计相关的函数
ViewModel文件,通过调用类对象的函数来实现对应的命令
View文件,通过Binding将ViiewModel的数据与命令进行绑定,实现双向通知
2022/1/7
1. 剖析RelayCommand类和RaisePropertyChanged()
自定义MyCommand
MyCommand类,
public class MyCommand : ICommand
{
Action executeAction;
public MyCommand(Action action)
{
executeAction = action;
}
public event EventHandler CanExecuteChanged;//命令状态发送变化时触发
public bool CanExecute(object parameter)//判断是否可以执行
{
return true;
}
public void Execute(object parameter)//执行代码
{
executeAction();
}
}
VM代码,
public class MainViewModel
{
public MainViewModel()
{
ShowCommand=new MyCommand(Show);
}
public MyCommand ShowCommand { get; set; }
public void Show()
{
MessageBox.Show("点击了按钮");
}
}
VM自继承INotifyPropertyChanged
VM代码,
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
ShowCommand=new MyCommand(Show);
}
public MyCommand ShowCommand { get; set; }
public void Show()
{
Name = "new name do you like?";
}
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); }
}
}
为了减轻代码量 可实现VMBase,
public class VMBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]String propertyName="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2. 文件的创建写入与打开读取
文件写入
FileStream fs=new FileStream(@"D:\Vs code\WpfApp3\save.txt", FileMode.OpenOrCreate,FileAccess.Write);
StreamWriter sw=new StreamWriter(fs);
sw.Flush();
sw.BaseStream.Seek(0, SeekOrigin.Begin);
sw.Write(res);
sw.Flush();
sw.Close();
文件读出
FileStream fs = new FileStream(@"D:\Vs code\WpfApp3\save.txt", FileMode.OpenOrCreate, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
sr.BaseStream.Seek(0, SeekOrigin.Begin);
string res = sr.ReadLine();
sr.Close();
3. 学习SVN
2022/1/10
阅读工程代码
2022/1/11
1. Messenger类
引用类库,
using GalaSoft.MvvmLight.Messaging;
注册收件人,
Messenger.Default.Register<string>(this, "token1", Show);
发送信息给收件人,
Messenger.Default.Send(name, "token1");
2. MVVM的扩展工具包
3. 静态资源与动态资源的区别
本质:动态资源会根据资源的改变而实时改变,但是静态资源不会
.xaml
<Window.Resources>
<SolidColorBrush x:Key="solidbrush" Color="Pink"/>
</Window.Resources>
<StackPanel>
<Button Content="Update" Click="Button_Click" Margin="10"/>
<Button Content="Button1" BorderBrush="{StaticResource solidbrush}" BorderThickness="10" Margin="10"/>
<Button Content="Button2" BorderBrush="{DynamicResource solidbrush}" BorderThickness="10" Margin="10"/>
</StackPanel>
.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Resources["solidbrush"]=new SolidColorBrush(Colors.Green);
}
4. 资源字典
目的:为使多个窗口使用相同的资源
添加ResourceDictionary Buttonstyle.xaml,
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="solidbrush" Color="Pink"/>
</ResourceDictionary>
App.xaml并入资源字典,
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Buttonstyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
cs使用以下代码查找资源,
var solidbrush = App.Current.FindResource("solidbrush");
5. 动画
双精度动画,
private void btn_Click(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new DoubleAnimation(); //创建一个双精度动画
animation.From = btn.Width; //动画初始值
animation.To = btn.Width - 30; //动画最终值
//animation.By=-30; //代替初始值与最终值
animation.Duration = TimeSpan.FromSeconds(2); //动画持续时长
animation.AutoReverse = true; //动画是否还原
animation.RepeatBehavior = RepeatBehavior.Forever; //动画动作是否反复
//animation.RepeatBehavior = new RepeatBehavior(3)
animation.Completed+=Animation_Completed; //添加动画完成函数
btn.BeginAnimation(Button.WidthProperty, animation);
}
private void Animation_Completed(object sender, EventArgs e)
{
btn.Content="动画已完成";
}
6. Prism框架×
7. 网易云界面教程
1)可以借助矢量图标库和特殊符号完成界面设计
2)注意Grid用于块整体设计
3)借助截图工具与取色器进行界面模仿
2022/1/12
对项目按照MVVM分层看,了解清楚FX的每个功能,熟悉客户端的功能模块
2022/1/13
1. WebSocket与HTTP的关系
WebSocket使用HTTP进行一次握手,然后走自己的协议,使用HTTP建立的那条TCP连接
2. WebSocket与Socket的区别
WebSocket区分服务器与客服端,Socket不区分
WebSocket实现了服务器的主动信息推送
WebSocket是一种应用层的协议,而Socket只是对TCP/UDP作封装
3. 基于SignalR的网络聊天室
2022/1/14
1. 进程通信
2. 线程的创建与通信
线程的同步与异步,
Action<int, int> add = (int x, int y) => //定义一个方法
{
MessageBox.Show((x + y).ToString());
};
add.Invoke(5,6); //该Action在当前线程同步运行
add.BeginInvoke(7, 8,null,null);//该Action另外创线程异步运行
}
3. VS在指定文件快速查找变量 Ctrl+F
4. Action与Func(对委托的一种泛型封装)
Func有返回值,最多包含16个泛型参数,加一个返回值
Action无返回值,最多包含16个泛型参数
匿名函数,
Func<int, int, int> add1 = delegate (int x, int y)
{
return x + y;
};
Action<int, int> add2 = delegate (int x, int y)
{
MessageBox.Show((x + y).ToString());
};
Lamada表达式,
Func<int, int, int> add1 = (int x, int y)=>
{
return x + y;
};
Action<int, int> add2 = (int x, int y) =>
{
MessageBox.Show((x + y).ToString());
};
5. 异步线程结束后的回调函数AsyncCallback
Action<int, int> add = (int x, int y) =>
{
MessageBox.Show((x + y).ToString());
};
AsyncCallback callback = arg =>
{
MessageBox.Show(arg.AsyncState.ToString());
MessageBox.Show("异步线程已结束");
};
add.BeginInvoke(7, 8, callback, "Sunday");
//函数的参数意义
BeginInvoke(
T args,
AsynCallback callback,
Object object
}
6. 观察异步线程的执行状况
IAsyncResult asyncResult=action.BeginInvoke("文件上传",null,null);
while(!asyncResult.IsCompleted)
{
label.Content="文件还在上传...";
}
label.Content = "文件已经上传";
7. 阻塞当前线程进行等待
IAsyncResult asyncResult=action.BeginInvoke("文件上传",null,null);
asyncResult.AsyncWaitHandle.WaitOne();//阻塞当前线程,直到异步线程结束
asyncResult.AsyncWaitHandle.WaitOne(-1);//永远阻塞,直到异步线程结束
asyncResult.AsyncWaitHandle.WaitOne(1000);//阻塞1000ms
MessageBox.Show("文件上传完成");
8. 获取线程的返回值
//同步线程返回值的获取办法
int res1 = func();
int res2 = func.Invoke();
//异步线程返回值的获取办法
IAsyncResult asyncResult = func.BeginInvoke(null, null);
int res3=func.EndInvoke(asyncResult);
2022/1//17
1. Thread类
//.NetFramework 1.0 1.1
ThreadStart threadstart = () =>
{
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:Start");
Thread.Sleep(2000);
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}:End");
};
Thread thread = new Thread(threadstart);
thread.Start();
Thread的优点,
1. API极其丰富,可以很彻底地让操作系统操控线程
Thread的缺点,
2. 线程资源是操作系统的,对其过度干扰可能会造成死机
2. ThreadPool类
//.NetFramework 2.0 (新的CLR Common language Runtime)
WaitCallback callback = o =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} ThreadPoll:Start");
Thread.Sleep(2000);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} ThreadPoll:End");
};
ThreadPool.QueueUserWorkItem(callback);
ThreadPool的特点,
主线程等待
ThreadPool的优点,
池化管理线程资源,实现自主地线程申请与释放,减少线程频繁创建销毁的次数
ThreadPool的缺点,
提供太少的API,就线程的执行顺序控制十分不方便
3. Parallel类
//.NetFramework 2.0
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} MainThread:Start");
Parallel.Invoke(() =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:Start1");
Thread.Sleep(2000);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:End1");
},
() =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:Start2");
Thread.Sleep(2000);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:End2");
},
() =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:Start3");
Thread.Sleep(2000);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:End3");
}
);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} MainThread:End");
Parallel的特点,
主线程等待
Parallel的优点,
1. 产生多线程后,主线程不闲置而是一起干活
2. 可以通过ParallelOptions轻松控制最大并发线程数
3. 线程全部由线程池生成
4. Task类
//.NetFramework 3.0
Action action = () =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:Start1");
Thread.Sleep(2000);
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} Parallel:End1");
};
Task task=new Task(action);
task.Start();
Task的特点,
主线程不等待
Task的优点,
1. 线程全部由线程池生成
2. 提供了丰富的API
5. Task类等待指定的线程数组(即保证顺序性)
List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() => this.Coding("111", "111")));
tasks.Add(Task.Run(() => this.Coding("222", "222")));
tasks.Add(Task.Run(() => this.Coding("333", "333")));
tasks.Add(Task.Run(() => this.Coding("444", "444")));
Task.WaitAll(tasks.ToArray());//阻塞主线程直到所有任务完成
Task.WaitAll(tasks.ToArray());//阻塞主线程直到任意任务完成
缺点,
因为阻塞主线程,所以会有卡界面的问题
解决1,
为当前等待任务再创一个线程,这样就不会卡界面了
但是,
尽量最好不要线程套线程,而让每个线程作为一个简单的工作单元
另外创建的等待线程如果想要更新主界面,还需要切换主线程
解决2,
使用TaskFactory类的ContinueWhenAll/ContinueWhenAny方法,
TaskFactory taskFactory = new TaskFactory();
taskFactory.ContinueWhenAll(tasks.ToArray(), tArray =>
{
Console.WriteLine($"This {Thread.CurrentThread.ManagedThreadId} MainThread:End");
});
6. 循环创建线程的多线程安全问题
for(int i = 0; i < 5; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"This is{i} {k} Start...{Thread.CurrentThread.ManagedThreadId}");
});
}
输出结果
This is5 0 Start...3
This is5 3 Start...5
This is5 4 Start...5
This is5 1 Start...4
This is5 2 Start...3
1. 为什么全是5?
主线程作i++的操作,匿名函数作输出操作,由于主线程执行的太快,导致i全是5
2. 为什么k是0 1 2 3 4?
因为实际上主线程有5个k,5个k分别被5个线程打印,它们互不干扰,所以能正确输出
7. 解决线程安全问题Lock
List<int> intList = new List<int>();
for(int i = 0; i < 10000; i++)
{
Task.Run(() =>
{
lock (LOCK)
{
intList.Add(i);
}
});
}
Thread.Sleep(5000);
Console.WriteLine(intList.Count);
private static readonly object LOCK = new object();
lock(LOCK)
等价于,
Monitor.Enter(LOCK);
Monitor.Exit(LOCK);
lock原理是锁定引用,即一块内存地址,不能锁定值类型或null
看完4-3
2022/1/18
查看倍速工程代码
2022/1/19
1. 倍速工程代码移动
2. 使用ItemControl
<Grid>
<Button x:Name="btn" Width="100" Height="40" Click="btn_Click" Content="1.0x"/>
<Popup x:Name="popup" Placement="Top" PlacementTarget="{Binding ElementName=btn}" StaysOpen="False">
<StackPanel>
<ItemsControl ItemsSource="{Binding Lists}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Width="100"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="speedgroup">
<RadioButton.Template>
<ControlTemplate>
<Grid x:Name="grid" Background="Black">
<TextBlock x:Name="block" Text="{Binding str1}" Foreground="White" HorizontalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="RadioButton.IsMouseOver" Value="True">
<Setter TargetName="grid" Property="Background" Value="Gray"/>
</Trigger>
<Trigger Property="RadioButton.IsPressed" Value="True">
<Setter TargetName="block" Property="Foreground" Value="Orange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Popup>
</Grid>
2022/1/23
1. 不要擅自修改JCE文件,而是去OA下载
jce文件去OA找到JCE管理中心,
下载对应的文件名对应的版本,
生成依赖,
然后解压找到自己需要在文件进行替换
2. 提交文件
Commit修改的文件夹,
将自己另外添加的文件使用Add,
在提交前通过SVN对比修改前后的代码