<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected,Mode=TwoWay}" />
<EventSetter Event="MouseDoubleClick" Handler="Button_MouseDoubleClick"></EventSetter>
</Style>
</ListView.ItemContainerStyle>
public static ChildItem FindVisualChild<ChildItem>(DependencyObject obj) where ChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is ChildItem)
return (ChildItem)child;
else
{
ChildItem childOfChild = FindVisualChild<ChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
例如寻找一个listbox中的scrollviewer:
ScrollViewer s = FindVisualChild<ScrollViewer>(this.listbox1);
这个blend里的东西,添加对Microsoft.Expression.Drawing的引用
参考:http://www.myexception.cn/c-sharp/1456925.html
场景:比如你写了一个框架,里面有个记录日志的委托Log,在自己框架是异步调用的,不想别人的实现占用框架执行其他方法的时间,需要别人去实现方法怎么记录日志,当别人的实现方法有异常时,可能会使你的框架崩溃,这时候的问题是:如何捕获BeginInvoke的异常?
答案是,BeginInvoke后面有个Callback函数,在那里可以捕获异常
且看下面的Demo:
public class Test
{
///
/// 模拟框架的Log日志
///
public Action<string> Log;
public void TestLog(string msg)
{
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]:enter testlog,msg:{msg}");
Log.BeginInvoke(msg,Callback,null);
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]:exit testlog,msg:{msg}");
}
private void Callback(IAsyncResult ar)
{
//捕获BeginInvoke的异常
try
{
Log.EndInvoke(ar);
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]:Callback异常,{e.Message}");
}
}
}
调用:
class Program
{
static void Main(string[] args)
{
Test test = new Test();
test.Log += TestMethod;
List<Task> tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
Task task = Task.Factory.StartNew(x =>
{
int j = (int)x;
test.TestLog(j.ToString());
}, i);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
Console.Read();
}
static void TestMethod(string msg)
{
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]:TestMethod,enter...");
Thread.Sleep(5000);
//模拟异常
int i = 0;
int j = 10 / i;
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}]:TestMethod,msg:{msg}");
}
}
结果:
异常都被捕获到了。
这个场景之前的处理方式是启动一个Task,在Task中用Try…catch,因为日志记录过多,且是多线程操作,每次启动Task感觉开销过大,所以就用BeginInvoke
使用Winform或者WPF进行双工通讯时,可能有死锁发生。解决办法:
https://www.cnblogs.com/artech/archive/2007/03/29/692032.html
class Program
{
static void Main(string[] args)
{
ITest test = new Test();
Test test1 = new Test();
test.Show();
test1.Show();
Console.Read();
}
}
public interface ITest
{
void Show(int a=100);
}
public class Test : ITest
{
public void Show(int a = 500)
{
Console.WriteLine(a);
}
}
平时我们定义了全局样式(没有key的),有时候需要继承这个全局,再增加点东西,可以用下面的方法
<Style TargetType="{x:Type StackPanel}" BasedOn="{StaticResource {x:Type StackPanel}}">
<!-- ... -->
</Style>
///
/// 设置左键单击弹出菜单的按钮
///
public class ButtonEx:System.Windows.Controls.Button
{
private ContextMenu menu;
public ButtonEx()
{
this.Loaded += ButtonEx_Loaded;
this.Click += ButtonEx_Click;
}
private void ButtonEx_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.menu.PlacementTarget = this;
this.menu.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
this.menu.HorizontalOffset = 0;
this.menu.VerticalOffset = this.ActualHeight;
this.menu.IsOpen = true;
}
private void ButtonEx_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
if (this.ContextMenu != null)
{
this.menu = ContextMenu;
this.ContextMenu = null;
}
}
}
SELECT memtyp_id as ID, display_name AS DisplayName, COALESCE(type_description,'NULL') as type_description from test
COALESCE(type_description,'NULL')
就是type_description为空时,返回“NULL”字符串的意思
ORACLE中相应的函数是NVL
,MySql中相应的函数是IFNULL
WPF中,Win10下系统自带的窗体是这样的:
在Win7下可能更难看。有时候我们需要在最上方的标题栏上加一些东西或者自定义标题栏,一般的做法是:设置WindowStyle="None"
然后自己加一个标题栏上去,但是这样做的话会屏蔽掉Window很多自带的功能(例如双击标题栏最大化,调整Window的大小等),所以不建议这样做。
我想说的是另一种方法:
设置如下附加属性,也能达到效果,而且完美的不损失Window原有的功能:
<Window.Style>
<Style TargetType="Window" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CornerRadius="0"
GlassFrameThickness="1"
UseAeroCaptionButtons="False"
NonClientFrameEdges="None" />
</Setter.Value>
</Setter>
</Style>
</Window.Style>
这样设置之后,展现出来的窗体的标题栏就不见了,但是完美的继承了Window的所有功能,这样我们再自定义标题栏就可以
参考:https://www.cnblogs.com/dino623/p/CustomWindowStyle.html
https://www.cnblogs.com/huaxia283611/p/wpf-maximize-windowchrome.html
例如将ComboBox 的选择改变事件绑定到ViewModel的SelectChangedCmd:
<ComboBox >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectChangedCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
引用System.Windows.Interactivity.dll
后,需要引入:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
var view= System.Windows.Data.CollectionViewSource.GetDefaultView(this.FileList);
view.Filter=...
这样,先获取ObserverableCollection的view,然后设置view的Filter即可实现过滤
首先建一个Model,里面包含Index属性,让Index与DataGrid的一列绑定:
<DataGrid Grid.Row="2" x:Name="dataGrid" ItemsSource="{Binding ConfigList}" Margin="2" RowHeaderWidth="80">
<DataGrid.Columns>
<DataGridTextColumn Header="序号" Width="80" Binding="{Binding Index}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
处理DataGrid的RowLoading事件:
this.dataGrid.LoadingRow += DataGrid_LoadingRow;
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
var model = e.Row.DataContext as HtmlSwitchConfigEditModel;
if (model != null)
{
model.Index = e.Row.GetIndex();
}
}
<TextBox x:Name="tbTrayCode" Width="150" Height="30" langtian:TextBoxHelper.Watermark="等待扫码..." IsReadOnly="{Binding ElementName=cbManualScanBatCode,Path=IsChecked,Converter={StaticResource BoolInverseConverter}}" Text="{Binding TrayCode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding TrayCodeCompleteInputCmd}"></KeyBinding>
</TextBox.InputBindings>
</TextBox>
public int Init(WorkInfo workInfo, IEnumerable<BatRecord> batRecoreds)
{
using (var ctx = freeSql.CreateDbContext())
{
ctx.Orm.Insert(workInfo).ExecuteAffrows();
ctx.Orm.Insert(batRecoreds).ExecuteAffrows();
return ctx.SaveChanges();
}
}
这样操作不同的表使用事务会报数据库被锁住
public int Init(WorkInfo workInfo, IEnumerable<BatRecord> batRecoreds)
{
using (var ctx = freeSql.CreateDbContext())
{
ctx.Orm.Insert(workInfo).ExecuteAffrows();
return ctx.SaveChanges();
}
using (var ctx = freeSql.CreateDbContext())
{
ctx.Orm.Insert(batRecoreds).ExecuteAffrows();
ctx.SaveChanges();
}
}
这样操作不会报死锁
<Grid x:Name="gridMask"></Grid>
<DataGridTextColumn Header="条码" Visibility="{Binding DataContext.ColumnConfig.IsBatCodeShow,Source= {x:Reference gridMask},Converter={StaticResource BoolToVisibleConverter}}"
Binding="{Binding BatCode}"></DataGridTextColumn>
1.使用ElementName和RelativeSource均不行,只有使用Source= {x:Reference ...}
才有用!!!
2.gridMask必须是DataGrid之后的元素,否则会报循环依赖的错误!!!!
static void Main(string[] args)
{
string path1 = "c:\\a\\b\\a.txt";
string path2 = "c:\\a\\b/a.txt";
string path3 = "c:/a/b/a.txt";
string path4 = "c:/a/b\\a.txt";
Uri uri1 = new Uri(path1);
Uri uri2 = new Uri(path2);
Uri uri3 = new Uri(path3);
Uri uri4 = new Uri(path4);
Console.WriteLine(uri1==uri2);
Console.WriteLine(uri1==uri3);
Console.WriteLine(uri1==uri4);
Console.Read();
}
public enum Status
{
On,
Off
}
var v= Enum.Parse(typeof(Status), "1");
var v1 = Enum.Parse(typeof(Status), "Off");
Console.WriteLine(v.Equals(v1));
Console.Read();
<ObjectDataProvider x:Key="FlowTypes" MethodName="GetValues" ObjectType="{x:Type flowDataModel:FlowStepType}">
<ObjectDataProvider.MethodParameters>
<x:Type Type="flowDataModel:FlowStepType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ComboBox Width="100" Height="30" ItemsSource="{Binding Source={StaticResource FlowTypes}}" SelectedItem="{Binding SelectedFlowType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? false : value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && value.Equals(true) ? parameter : Binding.DoNothing;
}
}
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="2">
<RadioButton Content="手动发送" GroupName="11" IsChecked="{Binding FlowStartType,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource EnumToBooleanConverter},ConverterParameter={x:Static config:FlowStartType.ManualStart}}"></RadioButton>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="2">
<RadioButton Content="扫码发送" GroupName="11" IsChecked="{Binding FlowStartType,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource EnumToBooleanConverter},ConverterParameter={x:Static config:FlowStartType.ScanStart}}"></RadioButton>
<TextBlock Text="压床码+托盘码+确认码" Foreground="Gray" Margin="10,0,0,0" FontSize="10" VerticalAlignment="Bottom"></TextBlock>
</StackPanel>
</StackPanel>
注意:此处的RadioButton 不能在同级目录下,因为同级目录下的RatioButton本身具有互斥性,会导致绑定失败!!!
在启动程序时,加入如下代码:
//解决TextBox MVVM模式绑定时,不能输入小数点的问题
FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
看如下代码:
string str = "";
double? a = double.TryParse(str, out double v) ? v : null;
意思就是,如果字符串str转换为数字,如果转换失败,则a的值为空,否则a的值为str对应的数字的值
但是上面的写法会报错:
那怎么办,难道只能用最笨的if else吗:
string str = "";
double? a = null;
if (double.TryParse(str, out double v))
{
a = v;
}
作为一个有追求的程序员,不止满足于解决问题,于是可以写成下面这样:
string str = "";
double? a = double.TryParse(str, out double v) ? v : new double?();
1.首先,要保证有PDB文件
2.NLog的配置如下:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true"
internalLogFile="c:\nlog1.txt"
internalLogLevel="Debug"
autoReload="true"
>
<!-- 设定日志扩展(即从*.dll加载NLog扩展) -->
<extensions>
<!--添加methodname所在的程序集-->
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- 设定日志的目标输出 -->
<targets>
<!-- 定义输出模板:
type="File":这个记录方式为文件类型
fileName="${logDirectory}/All.log":表示输出到文件All.log中
layout="...":输出文件中错误的显示格式
${logDirectory}:为上述定义的路径
${longdate}:输出长日期 yyyy-MM-dd HH:mm:ss.ffff(例:2013-01-31 14:49:21.2120)
${level}:错误等级(由低到高为Trace,Debug,Info,Warn,Error,Fatal)
${newline}:输出 新的一行
${stacktrace}:输出 堆栈信息
${callsite:className=True:fileName=True:includeSourcePath=True:methodName=True}:输出 命名空间.类名.方法名(文件路径:行号)
${message}:输出错误信息-->
</targets>
<targets async="true">
<default-wrapper xsi:type="BufferingWrapper" bufferSize="1"/>
<!--exception 由程序去拼接信息,放入到message中-->
<target name="txt" type="File" fileName="./Log/${shortdate}/${processname}/${level}.log"
layout="${longdate} ${logger} THREAD[${threadid}] ${message} ${exception:format=tostring}"
archiveAboveSize="1024000"
maxArchiveFiles="10" />
</targets>
<!-- 设定日志的路由规则 -->
<rules>
<!--客户端的log-->
<logger name="*" minlevel="trace" writeTo="txt"/>
</rules>
</nlog>
${exception:format=tostring}
这一句就是输出异常的详细信息。输出的结果如下:
https://docs.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/how-to-customize-the-thumb-size-on-a-scrollbar?view=netframeworkdesktop-4.8
想实现的效果如下,展开时:
蓝色为GridSplitter,可以拖动。
折叠时:
发现,折叠后再展开(均带有GridLengthAnimation动画),GridSplitter不能拖动了。相关代码如下:
折叠动画:
orignalLeftWidth = this.LeftWidth;
GridLengthAnimation ani = new GridLengthAnimation();
ani.From = orignalLeftWidth;
ani.To = new GridLength(this.HideWidth);
ani.Duration = duration;
ani.Completed += (s, e) =>
{
this.gridShow.Visibility = Visibility.Collapsed;
this.gridHide.Visibility = Visibility.Visible;
};
this.BeginAnimation(CommonLayoutWindow.LeftWidthProperty, ani);
展开动画:
this.gridShow.Visibility = Visibility.Visible;
this.gridHide.Visibility = Visibility.Collapsed;
GridLengthAnimation ani = new GridLengthAnimation();
ani.From = new GridLength(this.HideWidth);
ani.To = orignalLeftWidth;
ani.Duration = duration;
this.BeginAnimation(CommonLayoutWindow.LeftWidthProperty, ani);
这样,运行程序之后,发现第一次可以拖动,折叠然后展开之后,就不能拖动了
参考:https://docs.microsoft.com/zh-cn/dotnet/desktop/wpf/graphics-multimedia/how-to-set-a-property-after-animating-it-with-a-storyboard?redirectedfrom=MSDN&view=netframeworkdesktop-4.8#code-snippet-4
此文章提到的问题与当前遇到的问题一致,就是动画完成后,更改动画绑定的依赖属性无效,因为动画还在影响此依赖属性。
方法有三种,请参看具体的文章。本文使用第一种解决方案:将动画的 FillBehavior 属性设置为 Stop
更改后的代码:
折叠动画:
orignalLeftWidth = this.LeftWidth;
GridLengthAnimation ani = new GridLengthAnimation();
ani.From = orignalLeftWidth;
ani.To = new GridLength(this.HideWidth);
ani.Duration = duration;
ani.FillBehavior = System.Windows.Media.Animation.FillBehavior.Stop;
ani.Completed += (s, e) =>
{
this.gridShow.Visibility = Visibility.Collapsed;
this.gridHide.Visibility = Visibility.Visible;
this.LeftWidth = new GridLength(this.HideWidth);
};
this.BeginAnimation(CommonLayoutWindow.LeftWidthProperty, ani);
展开动画:
this.gridShow.Visibility = Visibility.Visible;
this.gridHide.Visibility = Visibility.Collapsed;
GridLengthAnimation ani = new GridLengthAnimation();
ani.From = new GridLength(this.HideWidth);
ani.To = orignalLeftWidth;
ani.Duration = duration;
ani.FillBehavior = System.Windows.Media.Animation.FillBehavior.Stop;
ani.Completed += (s, e) =>
{
this.LeftWidth = orignalLeftWidth;
};
this.BeginAnimation(CommonLayoutWindow.LeftWidthProperty, ani);
var settings = new Newtonsoft.Json.JsonSerializerSettings()
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
};
settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter(new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy(), false));
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model, settings);
写一个万能的泛型方法,能够打印出类型的名称:
static void Print<T>()
{
Console.WriteLine(nameof(T));
}
测试:
internal class Program
{
static void Main(string[] args)
{
Print<Person>();
Print<int>();
Console.Read();
}
static void Print<T>()
{
Console.WriteLine(nameof(T));
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
WTF,竟然不是想要的结果,原因是nameof
在编译器就已经确定了名称,而不是在运行时
修改为如下代码:
static void Print<T>()
{
Console.WriteLine(typeof(T).Name);
}