摘要: 发布一个小工具,定时关机。该程序基于.Net4制作,采用WPF技术,占用空间小,就一个文件,151kb,因为功能很简单:倒计时关机:设置一个时间,格式为xx:xx:xx,系统将在您设定的小时,分钟,秒后自动关机。定时关机:设置一个时间,格式为xx:xx:xx,系统将在你设置的时间自动关机。设置完成后可关闭此小程序。需要Microsoft .NET Framework 4 Client Profile支持。下载地址:http://luacloud.com/2011/auto-shutdown.html阅读全文
posted @ 2011-11-25 12:54 luacloud 阅读(713) 评论(6) 编辑

项目是老项目的维护,已经三年的东西了,虽然这期间维护的时候尝试过修改查询方式以提高效率,但是领导说怕出问题,所以一直搁置了,这次,领导打算全部进行优化,做一次大的调整,我就顺便记录一下之前尝试的优化方式与这一次优化的总结。

2009年刚出现LINQ TO SQL的时候,我们这个采购的项目就采用了这个东西,当时感觉,这个东西太好用了,而且也因为之前一直做JAVA,算是.Net的初学者,所以感觉微软的东西很容易上手,好方便啊。所以边学边就写了那个项目,做完后一直用着,有过几次修改,但没大大变动,直到后来服务器升级,我又看了一次,发现查询的时候是在是慢的可以,一个结果只有1000+的数据,查询到显示竟然用了15秒以上,这真的是难以忍受。

此时对LINQ TO SQL也有了一定的了解,也知道了一些方法如何优化,但是,受众们没提出我们也懒的修改,也怕修改出问题,就放着不动,现在他们也说太慢了,我们也觉得,是时候修改一下了,毕竟那套东西实在是难看之极,不是说界面,而是后台实现的代码,这次先拿一个页面开刀。

这个页面是采购单独页面,关联了一个项目表和一个价格表,查询的时候用到了项目表用来显示项目名称,当年用了这样的查询方式:

 1 var matches = from t in db.tRequestInfo
2 where
3 (pmbool ? t.tProjectInfo.PMUserID == pmid : true) &&
4 (ProjectNameText.Text != "" && ProjectNameText.Text != null ? t.tProjectInfo.ProjectName.Contains(ProjectNameText.Text) : true) &&
5 (RequestNoText.Text != "" && RequestNoText.Text != null ? t.RequestNo.Contains(RequestNoText.Text) : true) &&
6 (FillDateChk.Checked ? (Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateStart.Text.Trim())) >= 0 && Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateEnd.Text.Trim())) <= 0) : true) &&
7 (CheckStatChk.Checked ? t.State == false : true)
8 group t by new
9 {
10 t.RequestNo
11 }
12 into p
13 select new
14 {
15 OrderID = p.First().ID,
16 RequestNo = p.Key.RequestNo,
17 Url = HttpUtility.UrlEncode(p.Key.RequestNo),
18 ProjectName = p.First().tProjectInfo.ProjectName,
19 FillDate = p.First().FillDate,
20 State = p.First().State ? "<font color=grey>未生成价格确认单</font>" : "<font color=green>已生成价格确认单</font>"
21 };
22 if (matches.Count() > 0)
23 {
24 Msg.Text = "总共有【<font color=\"red\">" + matches.Count() + "</font>】条记录。";
25 }
26 else
27 {
28 Msg.Text = "未查询到任何数据,请确认查询条件是否正确。";
29 }
30 RequestData.DataSource = matches.OrderByDescending(t => t.OrderID);
31 RequestData.DataBind();

上面让我觉得最BT的是时间比较多方式,因为那个时候用了第三方的控件,而且当时的领导把数据库中日期字段设计成了nVarchar类型的,所以,我就找出了那么个比较大方式,当时就这样实现了查询功能。其实就连上面的语句都还是后来修改过了的。在绑定的时候使用OrderByDescending方式排序是因为在上上面LINQ语句中的排序会因为GroupBy的关系失效。

上面的查询我保存成了一个方法ShowRequestList()来调用,protected void Page_Load(object sender, EventArgs e)中调用了此方法用来默认显示。前台使用的GridView显示的数据,因为是.Net3.5的项目,所以我想,大多数都是使用这样的方式吧。

后来发现慢的要死,就找问题出在哪,后来了解到,是因为matches.Count()方法的调用,因为每调用一次,就到数据库中查询一次,所以上面的语句,调用了两次的Count方法,下面还排序然后绑定了一次,所以,查询了三次。

这样的话肯定会影响速度啊。领导前天告诉我们,使用微软的性能测试工具发现慢的问题还出现在调用DataBind方法的时候,我查了一下,因为调用DataBind方法其实后是将结果集使用IEnumeable方式循环,调用MoveNext来绑定到前台的GridView。没调用一次MoveNext也会进行一次查询。但是这个不好控制。所以,经过这样的查询方式,慢是必须的了。

那么我进行了第一次的优化,众所周知,SQL中Group BY语句的效率是非常低的,LINQ中也一样,所以能不用尽量不要用,那么我就不用;我想到了去重,虽然字段很多,但是我只用到了RequestNo、ProjectName、FillDate、和State这几个字段,OrderID是用来排序的,前台的数据显示并没有用到,所以去掉OrderID,只剩下这四个字段,那么我就是用去重功能,经过验证,确实可行:

 1 using (PMSDBDataContext db = new PMSDBDataContext())
2 {
3 var matches = (from t in db.tRequestInfo
4 where
5 (pmbool ? t.tProjectInfo.PMUserID == pmid : true) &&
6 (ProjectNameText.Text != "" && ProjectNameText.Text != null ? t.tProjectInfo.ProjectName.Contains(ProjectNameText.Text) : true) &&
7 (RequestNoText.Text != "" && RequestNoText.Text != null ? t.RequestNo.Contains(RequestNoText.Text) : true) &&
8 (FillDateChk.Checked ? (Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateStart.Text.Trim())) >= 0 && Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateEnd.Text.Trim())) <= 0) : true) &&
9 (CheckStatChk.Checked ? t.State == false : true)
10 select new
11 {
12 RequestNo = t.RequestNo,
13 Url = HttpUtility.UrlEncode(t.RequestNo),
14 ProjectName = t.tProjectInfo.ProjectName,
15 FillDate = t.FillDate,
16 State = t.State ? "<font color=grey>未生成价格确认单</font>" : "<font color=green>已生成价格确认单</font>"
17 }).Distinct();
18 int? count = matches.Count();
19 if (count.HasValue)
20 {
21 Msg.Text = "总共有【<font color=\"red\">" + count.Value + "</font>】条记录。";
22 RequestData.DataSource = matches;
23 RequestData.DataBind();
24 }
25 else
26 {
27 Msg.Text = "未查询到任何数据,请确认查询条件是否正确。";
28 }
29 }

将Count方法的值使用一个变量保存,避免再次调用,速度快了很多,但是我想,应该还可以继续优化掉一些。我有想到DataBind会影响到性能,因为我使用的是GridView自带的分页,所以有一个事件,要写成如下形式:

1     protected void RequestData_PageIndexChanging(object sender, GridViewPageEventArgs e)
2 {
3 RequestData.PageIndex = e.NewPageIndex;
4 RequestData.DataBind();
5 }

这里面还做了一次绑定,因为如果不写,那么前台点分页要点两次才会分页,有说把Page_Load方法中的ShowRequtstList放到Pre_Render中好像是这么写的,但我觉得应该不是这样的。于是又各种查资料,无果,很郁闷,因为都说是使用这样的方法:将新页面给空间,然后重新绑定,更有甚者会在这重新调用一次ShowRequestList方法来进行绑定,本来效率就慢,在增加一套查询,难怪会慢死。所以我打算去掉RequestData.DataBind()。于是我就去掉了,不可否认的是,分页的确掉了,但是也要点两次,无意中我想到,当时为了页面不刷新,我使用过.Net自带的Ajax来进行局部刷新,可是后来我发现GridView自带无刷新分页。我就想如果使用自带的无刷新分页会不会好些?于是咱就来尝试吧,到aspx页面将GridView的EnableSortingAndPagingCallbacks属性设置为True。果然,问题解决了,这是一个偶然,将EnableSortingAndPagingCallbacks设置为True就能解决GridView点两次分页的问题。这样,分页方法中就不需要DataBind了:

1     protected void RequestData_PageIndexChanging(object sender, GridViewPageEventArgs e)
2 {
3 RequestData.PageIndex = e.NewPageIndex;
4 }

性能稍微又提升了一些。还能进行什么样的提升呢。我想到了搜索按钮,搜索按钮的Click时间中也调用了一次ShowRequestList方法,这样会不会造成,页面刷新调用的一次ShowRequestList方法,Click中也调用了ShowRequestList,等于调用了两次,那么就吧Click中的ShowRequestList删掉,什么都不写,一试,嗯,同样可以查询,一点都不影响。

但是还是觉得别扭,因为这句:

(FillDateChk.Checked ? (Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateStart.Text.Trim())) >= 0 && Convert.ToDateTime(t.FillDate.Trim()).CompareTo(Convert.ToDateTime(FillDateEnd.Text.Trim())) <= 0) : true) &&

这个是我最纠结的。不过也解决了,改成了这样:

(Convert.ToDateTime(t.FillDate) >= FillDateStart.Value) && (Convert.ToDateTime(t.FillDate) <= FillDateEnd.Value)

只是按照时间搜索的话很少,而且页面上FillDateChk而True的时候才使用时间查询,但是写在LINQ语句中,肯定是会被转换成SQL语句的。使用db.GetCommand(matches).CommandText查看生成的SQL语句就会发现,这个GetCommand是我昨天刚学来的。显示LINQ转换的SQL语句。

那么我想能不能动态的拼接SQL语句,找了很多这样的内容。但是都说用到了反射,性能会下降,能不用尽量不用。可是微软不会有了LINQ连这样动态的创建LINQ都不支持吧,性能还要下降。于是去翻CSDN和MSDN,不错,终于让我在一个角落里找到了一个方式。LINQTOSQL是延迟加载的。所以LINQ是可以拼接的,于是就改成了这样的写法:

using (PMSDBDataContext db = new PMSDBDataContext())
{
var query = from t in db.tRequestInfo select t;
do
{
//判断是否是项目经理
if (pmbool)
{
query = query.Where(t => t.tProjectInfo.PMUserID == pmid);
}
//判断是否要通过项目名称查询
if (!string.IsNullOrEmpty(ProjectNameText.Text))
{
query = query.Where(t => t.tProjectInfo.ProjectName.Contains(ProjectNameText.Text));
}
//判断是否要通过单号查询
if (!string.IsNullOrEmpty(RequestNoText.Text))
{
query = query.Where(t => t.RequestNo.Contains(RequestNoText.Text));
}
//判断是否勾选已生成价格确认单的采购单
if (CheckStatChk.Checked)
{
query = query.Where(t => t.State == false);
}
//判断是否要通过日期查询
if (FillDateChk.Checked)
{
query = query.Where(t => (Convert.ToDateTime(t.FillDate) >= FillDateStart.Value) && (Convert.ToDateTime(t.FillDate) <= FillDateEnd.Value));
}
} while (false);
//查询并生成新的匿名方法并去重并排序
var matches = query.Select(t => new
{
RequestNo = t.RequestNo,
Url = HttpUtility.UrlEncode(t.RequestNo),
ProjectName = t.tProjectInfo.ProjectName,
FillDate = t.FillDate,
State = t.State ? "<font color=grey>未生成价格确认单</font>" : "<font color=green>已生成价格确认单</font>"
}).Distinct().
OrderByDescending(t => Convert.ToDateTime(t.FillDate));
//并判断结果集中是否有数据
int? count = matches.Count();
if (count.HasValue)
{
Msg.Text = "总共有【<font color=\"red\">" + count.Value + "</font>】条记录。";
RequestData.DataSource = matches;
RequestData.DataBind();
}
else
{
Msg.Text = "未查询到任何数据,请确认查询条件是否正确。";
}
}

太好了!速度又提升了,很兴奋。可是这个时候还是没解决MoveNext的时候一直在查询的问题,我知道其实延迟加载的技术更好,但是我觉得这个地方并不需要,现在需要的是速度!越快越好。所以,手动的去掉延迟加载,很容易,只加一个地方:

 1 //查询并生成新的匿名方法并去重并排序
2 var matches = query.Select(t => new
3 {
4 RequestNo = t.RequestNo,
5 Url = HttpUtility.UrlEncode(t.RequestNo),
6 ProjectName = t.tProjectInfo.ProjectName,
7 FillDate = t.FillDate,
8 State = t.State ? "<font color=grey>未生成价格确认单</font>" : "<font color=green>已生成价格确认单</font>"
9 }).Distinct().
10 OrderByDescending(t => Convert.ToDateTime(t.FillDate)).ToArray();//在此处ToList()会提升很大的查询速度,是否有其他影响未知,也是取消了延迟加载

在这个地方ToArray就已经将结果集查出来了,所以MoveNext的时候就不用一直查询了,速度也就上去了。我也尝试了ToList,但是使用性能测试工具得出的结果是ToArray会更快,这个我就不清楚为什么了,如果哪位知道,请赐教,谢谢。

 

 20111216,今天又做了一下小的调整,这两天回复的不少,提了很多方法,很感谢你们,只是有时候好的方法会因为项目上的限制无法发挥,就我上面这样的修改,领导也认为修改太大,但是今天我又稍微的修了了一下,将LINQTOSQL的查询时间控制在100毫秒以内。我的机器小于70,很稳定的速度了,原来写的查询慢的原因,一个是因为使用了GroupBy,还有今天也发现Distinct也慢,即使是使用存储过程的方式。这次就将代码调整成为这样:

 1 using (PMSDBDataContext db = new PMSDBDataContext())
2 {
3 var query = db.tRequestInfo.Select(t => new { t.RequestNo, Url = HttpUtility.UrlEncode(t.RequestNo), PMUserID = t.tProjectInfo.PMUserID, ProjectName = t.tProjectInfo.ProjectName, t.State, GenerateState = t.State ? "<font color=grey>未生成价格确认单</font>" : "<font color=green>已生成价格确认单</font>", t.FillDate });
4 do
5 {
6 //判断是否是项目经理
7 if (pmbool)
8 {
9 query = query.Where(t => t.PMUserID == pmid);
10 }
11 //判断是否要通过项目名称查询
12 if (!string.IsNullOrEmpty(ProjectNameText.Text))
13 {
14 query = query.Where(t => t.ProjectName.Contains(ProjectNameText.Text));
15 }
16 //判断是否要通过单号查询
17 if (!string.IsNullOrEmpty(RequestNoText.Text))
18 {
19 query = query.Where(t => t.RequestNo.Contains(RequestNoText.Text));
20 }
21 //判断是否勾选已生成价格确认单的采购单
22 if (CheckStatChk.Checked)
23 {
24 query = query.Where(t => t.State == false);
25 }
26 //判断是否要通过日期查询
27 if (FillDateChk.Checked)
28 {
29 query = query.Where(t => (Convert.ToDateTime(t.FillDate) >= FillDateStart.Value) && (Convert.ToDateTime(t.FillDate) <= FillDateEnd.Value));
30 }
31 } while (false);
32 var matches = query.ToList().Distinct().OrderByDescending(t => Convert.ToDateTime(t.FillDate)).ToArray();
33 int? count = matches.Count();
34 if (count.HasValue){...}
35 }

主要是第32行当地方,先进行ToArray在进行Distinct和OrderByDescending,由于会出现“数据源不支持服务端的数据分页”所以在后面还要加一个ToArray。


这个就这样了,要说方式好,按照下面回复的那些前辈来说,并不好,但是,对于那些只使用LINQTOSQL方式的人来说,能给你们一点借鉴的东西,就行了;

我想,虽然不是好的方案,但是还是对人们有点帮助的吧。

更多内容请访问:http://luacloud.com/2011/another-linq-query-optimization.html



posted @ 2011-12-15 14:25 luacloud 阅读(1937) 评论(27) 编辑

MSDN中说ObservableCollection是一个动态的数据集合,在添加项、移除项或刷新整个列表的时候,此集合将提供通知。我是在WPF中用了这个,但是我在网上找资料的时候发现,有在WinForm中也用到的,我并没有去验证,使用方式应该一样吧,只是WPF中是提供与前台UI界面中的控件进行绑定操作时通知更新的,WinForm中怎么做就不知道了。

这个,上面MSDN那么说了,但是按照我的理解就是,ObservableCollection数据集中的条目有变动,就出发通知流程,引发更新操作。这是一个泛型类,ObservableCollection<T>的形式,我之前有一个例子,是使用ListBox做柱状图的,里面用到了ObservableCollection,里面提到一个ReptClass类,作为数据基类,ObservableCollection创建数据源,与前台的ListBox进行绑定,显示柱状图。可以参考《WPF-用ListBox做简单的柱状图》,这次还是用柱状图中的类做例子。

我今天上午一直思考一个问题,就是ObservableCollection集合中的单个条目值改变前台UI会不会更新,按照MSDN上的说法是会自动更新的,但是我试了一下,发现,只有增加条目,删除条目的时候会引发通知,当更改条目总某个值得时候并没有引发通知。

但是当我设置断点后发现,值确实改变了,只是UI并没有更新,现在的问题是,增加项、删除项,会引发通知;条目值发生变化,未进行通知。

我以为是我的写法有问题,我又查阅了一下资料,顺便看了一下今年夏天那个项目中我是如何操作的,然后发现了一个不同,就是作为基类的ReptClass,原来的写法是:

//数据类
public class ReptClass
{
public int count { set; get; }//数量
public Brush color { set; get; }//颜色
public string mouth { set; get; }//月份
public int height { get { return count * 3; } }//高度
}

但是,这里需要提到一个接口:INotifyPropertyChanged,MSDN中说他是向客户端发出某一属性已更改的通知。在MVVM框架中也用到了这样的组合方式。

于是我让上面的类继承此接口:

//数据类
public class ReptClass : INotifyPropertyChanged
{
private int _count;
public int count
{
set
{
_count = value;
this.Changed("count");
this.Changed("height");
}
get { return _count; }
}//数量
public Brush color { set; get; }//颜色
public string mouth { set; get; }//月份
public int height
{
get { return count * 3; }
}//高度
#region 属性更改通知
public event PropertyChangedEventHandler PropertyChanged;
private void Changed(string PropertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
#endregion
}

通过上面这样的更改,当我再次对条目进行更改的时候,UI就更新了。

比如,我要为每一项的count都加1,我可以这样写:

foreach (var item in listboxSource)
{
item.count += 1;
}

或者进行下面这样的操作

//移除mouth=十二月的条目
listboxSource.Remove(listboxSource.FirstOrDefault(t => t.mouth == "十二月"));
//移除第一个
listboxSource.RemoveAt(0);
//移除最后一个
listboxSource.RemoveAt(listboxSource.Count - 1);
//将第一个移动到最后
listboxSource.Move(0, listboxSource.Count - 1);

做个记录,防止下次用到的时候再忘了。

 

更多内容欢迎访问:http://luacloud.com/2011/ablout-observablecollection-2.html






posted @ 2011-12-13 13:40 luacloud 阅读(1546) 评论(15) 编辑

这是今年7、8月份的时候,那个项目中用到的,需要显示柱状图和饼图,我用到的是柱状图,因为是先做Demo所以,所以打算所有东西都是用WPF做而不使用第三方,一个是因为可能涉及到版权问题,也是为了维护和定制方便,是用WPF做这类的应用,界面和可定制性还是不错的,如果有一个好的美工的话就更好了,后来赶时间,就想到了用ListBox自己做一个,做完之后感觉还不错,虽然不是很美观,但是基本功能倒是实现了。

前台放置一个ListBox控件,用来显示柱状图,是用ListBox的自定义模板功能为柱状图设计样式:

<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden" DockPanel.Dock="Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom">
<ListBox ScrollViewer.CanContentScroll="False" ItemsSource="{Binding}" x:Name="LSRept">
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<StackPanel>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</StackPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="5,0">
<TextBlock Margin="0,20">
<Run Text="{Binding count}"/><Run Text="次"/>
</TextBlock>
<Rectangle Fill="{Binding color}" RenderTransformOrigin="0.5,1" x:Name="PART_Table" Width="32" Height="{Binding height}" HorizontalAlignment="Center" VerticalAlignment="Bottom">
<Rectangle.RenderTransform>
<ScaleTransform/>
</Rectangle.RenderTransform>
</Rectangle>
<TextBlock Text="{Binding mouth}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="80"/>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger SourceName="PART_Table" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0" Duration="0:0:0.15" From="1" To="1.1" Storyboard.TargetName="PART_Table" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"/>
<DoubleAnimation BeginTime="0" Duration="0:0:0.15" From="1" To="1.1" Storyboard.TargetName="PART_Table" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0" Duration="0:0:0.15" From="1.1" To="1" Storyboard.TargetName="PART_Table" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"/>
<DoubleAnimation BeginTime="0" Duration="0:0:0.15" From="1.1" To="1" Storyboard.TargetName="PART_Table" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</ScrollViewer>

binding的内容根据自己的需求进行更改,ControlTemplate标签下的内容就是需要显示的数据,也就是柱,颜色啊,数据的方式以及调整。

WPF的开发我建议使用MVVM的方式进行数据绑定,我上面的例子中显示的内容很简单,是次数/月份,每个住用不同的颜色区分,高度是次数*3的像素,基本上根据自己的一个公式足够了。

绑定的类这么写:

//数据类
public class ReptClass
{
public int count { set; get; }//数量
public Brush color { set; get; }//颜色
public string mouth { set; get; }//月份
public int height { get { return count * 3; } }//高度
}

这就是基础的类,根据这个类,建立数据源绑定到前台的ListBox,就实现了我的柱状图效果。

数据源:

ObservableCollection<ReptClass> listboxSource = new ObservableCollection<ReptClass>();
public RoadDesision()
{
InitializeComponent();
listboxSource.Add(new ReptClass() { count = 16, mouth = "一月", color = Brushes.Blue });
listboxSource.Add(new ReptClass() { count = 18, mouth = "二月", color = Brushes.Silver });
listboxSource.Add(new ReptClass() { count = 24, mouth = "三月", color = Brushes.SeaGreen });
listboxSource.Add(new ReptClass() { count = 15, mouth = "四月", color = Brushes.SlateBlue });
listboxSource.Add(new ReptClass() { count = 10, mouth = "五月", color = Brushes.SeaShell });
listboxSource.Add(new ReptClass() { count = 30, mouth = "六月", color = Brushes.SandyBrown });
listboxSource.Add(new ReptClass() { count = 28, mouth = "七月", color = Brushes.SteelBlue });
listboxSource.Add(new ReptClass() { count = 29, mouth = "八月", color = Brushes.SlateGray });
}
LSRept.ItemsSource = listboxSource;

这样就好了,前台柱状图就好了,鼠标移动到柱状图中的柱上面会有一个局部的放大效果,有需要的朋友,可以自己定制一个符合自己的。如果有美工好的,可以增加一些更好的效果。

 

更多内容请访问:http://luacloud.com/2011/wpf-listbox-chart.html



posted @ 2011-12-12 09:23 luacloud 阅读(1264) 评论(0) 编辑

先说同步与异步的区别,总说同步异步的,其实一直不清楚什么是同步异步,今天找了一下:

同步:当一个消息发送后,等对方回应后继续发送下一条指令。

异步:当一个消息发送后,不等对方回应就发送下一条。

异步:刚才被更正了,异步是,接收方不进行处理立即返回。

同步,举个例子,就是你给人打电话的时候,你说一句,对方回你一句,然后你在说下一句,交流嘛。

异步呢,你给一个人打电话,你一直在说。

而BeginInvoke与Invoke就是同步与异步。BeginInvoke是异步操作,Invoke是同步操作。而这两个方法中执行的是一个委托。

举一个代码中的例子,在WPF中有一个TextBlock控件,Name属性为txtContent有时候会出现跨线程的问题,这个什么时候出现,我现在不知道为啥模拟不出来了。但是我记得这样的写法,因为我感觉这样的写法就是为了防止出现这种问题的。是这样写的:

1 txtContent.Dispatcher.Invoke(new Action(() =>
2 {
3 txtContent.Text = "xxxxxxx";
4 }));

如果带有参数的话就这么写:

1 txtContent.Dispatcher.Invoke(new Action<string, string>((arg1, arg2) =>
2 {
3 txtContent.Text = arg1 + arg2;
4 }), "参数1", "参数2");

如果多个参数的话,Action泛型中增加就可以了。后面也相对的增加传递的值。

使用Dispatcher.Invoke就是所谓的同步更新,异步更新呢?就是吧Invoke换为BeginInvoke就是异步了,其实看了一些资料我还是没明白Invoke和BeginInvoke到底有什么区别。但是呢,更新界面的话,必须是UI线程,也就是谁创建了UI控件就由谁来更新,跨线程更新是肯定会报错的。所以,也就有了BeginInvoke和Invoke的方法了,使用这种方法调用的也就不报错了,因为这两个还是运行在UI线程上的。

 1 txtContent.Dispatcher.BeginInvoke(new Action<string, string>((arg1, arg2) =>
2 {
3 //此处写需要进行异步的操作
4 txtContent.Text = arg1 + arg2;
5 }), "参数1", "参数2");
6 //或这样写
7 Dispatcher.BeginInvoke(new Action<TextBlock>((arg_txt) =>
8 {
9 //此处写需要进行异步的操作
10 arg_txt.Text = "异步更新";
11 }), txtContent);

找这个理解的话,写在BeginInvoke中的委托更新要比Invoke好,因为Invoke是同步更新,要等任务结束后才会更新界面,BeginInvoke的话就不管了,异步嘛,不等,直接更新了。

昨天没写完,继续说。

但是上面的委托体内如果有耗时操作,还是会出现UI界面假死的状况,例如如下的代码:

1 txtContent.Dispatcher.BeginInvoke(new Action<string, string>((arg1, arg2) =>
2 {
3 //此处写需要进行异步的操作
4 for (long i = 0; i < 2147483647; i++)
5 txtContent.Text = arg1 + arg2 + i;
6 }), "参数1", "参数2");

如何才能更好的解决这种假死的情况呢?因为BeginInvoke和Invoke只是为了解决线程安全的问题,要解决假死的问题很多时候用到的都是BackgroupWorker。不多说了,但是我在网上查找资料的时候又找到了另外一种写法:

 1 Action ut = new Action(() =>
2 {
3 //耗时操作
4 for (long i = 0; i < Int16.MaxValue; i++)
5 txtContent.Dispatcher.Invoke(new Action(() =>
6 {
7 txtContent.Text = string.Format("{0}", i);
8 }));
9 });
10 IAsyncResult result = ut.BeginInvoke(new AsyncCallback(new Action<IAsyncResult>((asyncResult) =>
11 {
12 //耗时结束后的操作
13 if (asyncResult == null) return;
14 ut.EndInvoke(asyncResult);
15 })), ut);



这种写法与BackgroupWorker有什么区别呢,不清楚,毕竟我不是高手,在这条路上探索的一个程序员而已。只是我觉得,不用写那么多行了。







更多内容请访问:http://luacloud.com/2011/begininvkod-and-invoke.html

posted @ 2011-12-06 09:58 luacloud 阅读(2451) 评论(11) 编辑

今天突然想到一个判断session是否合法的做法,asp.net的,之前我们的做法是下面这样的形式的:

1 if (Session["UserID"] == "" || Session["UserID"] == null)
2 {
3 Response.Redirect("../Login.aspx?m=登录已超时,请重新登陆!");
4 }

一直我都觉得这个方法很不好,非常不好,但是一直也没找到好的方法,刚才突然就想到和匿名方法,结合??运算符,如果session为空,那么就不合法的了, 可以用来判断用户是否登录。

由于Session["UserID"]返回的是Object类型,如果是空的话,就会报空指针异常,以上面的形式来看,而且,这种判断登录状态的行为,在一些项目中是几乎每个页面都需要用到,所以可以提取出一个方法,与其他公有的静态方法一起放到一个类里面,写成如下形式:

1 /// <summary>
2 /// 判断是否登录成功,如果成功则返回session存的字符串,否则为空字符串
3 /// </summary>
4 public static Func<Object, string> isLogin = session => session as string ?? string.Empty;

Session里面可以存的Object,所以,可以是个字符串,可以是个数字,也可以是一个类或者一个集合。我上面这段代码假定存储的是一个字符串,调用的时候,就像是调用方法一样那个调用这个匿名的方法:

1 if (string.IsNullOrEmpty(isLogin(Session["UserID"])))
2 {
3 Response.Redirect("../Login.aspx?m=登录已超时,请重新登陆!");
4 }

您可以会说为何不直接使用string.IsNullOrEmpty来直接判断Session?那么我告诉你,这就如果Session中没有你进行判断的这个key会直接报空指针异常的。

如果是一个类呢?也很明显,session存储的比如说是一个User类,那么上面对代码就改成这样的形式:

1 public static Func<Object, User> isLogin = session => session as User ?? new User() { UserID = -1};

因为返回的类型是User,所以可以使用一个User类来进行接收返回的值,这样,在后面的操作中可以直接使用了。

1 User _user = isLogin(Session["UserID"]);
2 if (_user.UserID == -1)
3 {
4 //登录失败
5 }

我不清楚这样的方式好不好,但是我觉得这样的代码,我读起来更容易理解,操作起来也会方便一些。如果您有更好的方法,请您不吝指教。

 

更多内容请查看:http://luacloud.com/2011/asp-net-session-null-or-empty.html







posted @ 2011-12-02 13:53 luacloud 阅读(3112) 评论(23) 编辑
摘要: yield这个关键字是和迭代器挂钩的,而且是与return一起以yield return的形式合用的,用来返回迭代器中的条目。yield不能单独放在try-catch块中,如果try中有yield那么,这个try块后面不许跟着finally块;也不能出现在匿名方法中,所以,看起来yield似乎并不常用,但是也不是不用。我前面有一个关于迭代器的例子《C#中的迭代器基础》中就用到了。可以参考一下那个例子,但是这里要再说的一点是我后来看到的,yield是跟return一起使用的,形式为yield return xxx,一般来说单独的return在每个方法中只能存在一个。而yield则不同的是,可以出阅读全文
posted @ 2011-12-01 16:09 luacloud 阅读(264) 评论(0) 编辑
摘要: 迭代器是可以返回相同类型的值的有序序列的一段代码。迭代器可用作方法、运算符或 get 访问器的代码体。迭代器代码使用yieldreturn 语句依次返回每个元素。yieldbreak 将终止迭代。可以在类中实现多个迭代器。 每个迭代器都必须像任何类成员一样有唯一的名称,并且可以在 foreach 语句中被客户端代码调用,如下所示:foreach(int x in SampleClass.Iterator2){}。迭代器的返回类型必须为 IEnumerable、IEnumerator、IEnumerable(Of T) 或IEnumerator(Of T)。迭代器是 LINQ 查询中延迟执行行为阅读全文
posted @ 2011-12-01 15:13 luacloud 阅读(1294) 评论(13) 编辑
摘要: 在MSDN上面对new()解释说到是where字句的构造函数约束,带有new()约束的任何类型都必须有可访问的无参构造函数,正常来说C#创建的类默认都有一个无参的构造函数,即使你没有写,但是如果你写了一个有参数的构造函数后,那么就没有默认无参的那个了,就需要自己手动写一个。还是拿前两天的国籍那个举个例子: 1 /// <summary> 2 /// 国籍的接口 3 /// </summary> 4 public interface INationality 5 { 6 string Nationality 7 { 8 set; 9 }10 str...阅读全文
posted @ 2011-11-30 14:28 luacloud 阅读(1494) 评论(5) 编辑
摘要: global关键字,就是字面的意思,全局。其实有些时候会犯一些错误,就是类名取了一个跟系统类名雷同的情况,其实这是设计上的失误,但是会出现一个情况就是没改了,那么global关键字就起到了作用。如下代码: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace globalFunc 7 { 8 class Program 9 {10 static void Main(string[] args)11 ...阅读全文
posted @ 2011-11-30 10:39 luacloud 阅读(422) 评论(0) 编辑
摘要: 这个关于泛型约束的东西我看了几天了。一直没打看懂,我的领悟能力有点差,刚才突然明白了一点,泛型约束的意思就是说:类的泛型,只能是where字句后面所写的接口或类。这么说好像也有点不大明白,举个例子。我有一个接口,如下: 1 ///<summary> 2 /// 国籍的接口 3 ///</summary> 4 public interface INationality 5 { 6 string Nationality 7 { 8 set; 9 get;10 }11 string GetNationality();12 }然后该接口有...阅读全文
posted @ 2011-11-28 13:41 luacloud 阅读(3289) 评论(29) 编辑