WPF Image异步加载控件

ImageLoadingControl使用说明

控件提供Source属性,可Binding图片Url或Path

1
2
3
<ImageLoadingControl:ImageLoadingControl HorizontalAlignment="Left" Height="200" Margin="30,68,0,0" VerticalAlignment="Top" Width="200"  Source="{Binding ImageUrl1}"/>
<ImageLoadingControl:ImageLoadingControl HorizontalAlignment="Left" Height="200" Margin="303,68,0,0" VerticalAlignment="Top" Width="200" Source="{Binding ImageUrl2}"/>
<ImageLoadingControl:ImageLoadingControl HorizontalAlignment="Left" Height="200" Margin="556,68,0,0" VerticalAlignment="Top" Width="200" Source="{Binding ImageUrl3}"/>
1
2
3
4
5
6
private void button_Click(object sender, RoutedEventArgs e)
{
ImageUrl1 = @"https://pixabay.com/static/uploads/photo/2016/02/09/13/45/rock-carvings-1189288_960_720.jpg";
ImageUrl2 = @"https://pixabay.com/static/uploads/photo/2016/02/14/14/32/construction-1199586_960_720.jpg";
ImageUrl3 = @"c:\test.jpg";
}

下图分别显示了控件加载中,加载完成,加载失败三种状态:


WPF使用BitmapImage内存释放问题

在WPF中进行图片的相关操作是一件比较麻烦的事,并不是说它复杂,而是不注意的话很容易引起内存暴涨甚至溢出。关于BitmapImage使用的相关说明如下:

一、 创建方式

使用Uri设置BitmapImage会自动形成缓存,不关闭整个模块的话GC不会回收。 故如果在单个模块多次显示图片,不要使用这种方式:

var bitmap = new BitmapImage(new Uri(@"c:\test.bmp"));

建议通过流的方式加载图片:


WPF项目转COM组件

最近有一个项目需求,要把WPF开发的程序打包成COM组件供其他程序使用,WPF工程转COM并不困难,但有一些细节还是需要记录一下:

首先需要把应用程序转成类库:

需要注意当应用程序转换成类库后App.xaml就需要删除了,如果在App.xaml中做了启动控制或者全局资源字典,需要重新规划,如全局资源的加载方式,重复启动的判断等等。

还需要注意要把主窗体改成UserControl,否则组件会以窗口形式打开。


LinQtoEntity类型转换

使用EntityFrameWork时,经常会用到lambda表达式来做查询等操作,由于EF要根据表达式生成最终操作数据库的SQL,所以在表达式中加入其它方法如”parse”,”convert”可能会导致不被LinqToEntity识别,异常如下:

System.NotSupportedException: LINQ to Entities does not recognize the method int.Parse(System.String)

但在实际项目中往往会遇到实体字段类型与参数类型需要转换并比较的问题:

问题1: 字段int型与参数string型的比较

例:


Advanced Installer 11.4 使用教程

自从VS2012去掉了自家的Windows Installer改用InstallSheild之后,打包程序总是找不到满意的工具,最后投奔Advanced Installer,界面简洁,操作简单,功能也很强大。

使用Advanced Installer打包程序的常规步骤如下:

1 产品信息

![](/advanced_ installer/ai1.png)

如图所示,填写Product Name, Product Version 和 Product Company. 这里需要解释一下Product Version与Product Code、Update Code之间的关系:


WPF异步加载BitmapImage

在WPF中异步获取HTTP图片并赋值给Image控件,遇到诸多问题,如多线程队列,资源不释放等,最终琢磨出下面的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
Uri uri = e.Argument as Uri;

using (WebClient webClient = new WebClient())
{
webClient.Proxy = null;
webClient.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
try
{
byte[] imageBytes = null;

imageBytes = webClient.DownloadData(uri);

if (imageBytes == null)
{
e.Result = null;
return;
}
MemoryStream imageStream = new MemoryStream(imageBytes);
BitmapImage image = new BitmapImage();

image.BeginInit();
image.StreamSource = imageStream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();

image.Freeze();
imageStream.Close();

e.Result = image;
}
catch (WebException ex)
{
e.Result = ex;
}
}
};

worker.RunWorkerCompleted += (s, e) =>
{
BitmapImage bitmapImage = e.Result as BitmapImage;
if (bitmapImage != null)
{
myImage.Source = bitmapImage;
}
worker.Dispose();
};

worker.RunWorkerAsync(imageUri);

EntityFramework5.0与WCF使用遇到的问题

在使用EntityFramework5.0,并将其作为WCF放在服务端的时候遇到了如下问题:

1. EF在客户端与服务端之间传输问题

由于将EF放在服务端,所以类必须具有DataContract属性。

服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型。

只有声明为DataContract的类型的对象可以被传送,且只有成员属性会被传递,成员方法不会被传递。WCF对声明为DataContract的类型提供更加细节的控制,可以把一个成员排除在序列化范围以外,也就是说,客户端程序不会获得被排除在外的成员的任何信息,包括定义和数据。默认情况下,所有的成员属性都被排除在外,因此需要把每一个要传送的成员声明为DataMember,如下所示。


资源键Key

1. 什么是资源键

资源键x:Key用作创建和引用资源的唯一标识,作用类似于名称,常出现在ResourceDictionary中且要求必须为 ResourceDictionary 内的每项定义一个键。

例:

1
2
3
4
5
6
<ResourceDictionary>
<LinearGradientBrush x:Key="fadeBrush">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>
</ResourceDictionary>

但带有DataType的数据模板DataTemplate和带有TargetTypeStyle属于特殊情况,他们本身带有隐式键。


如何在WPF中停止线程

1. Abort的非及时性

使用多线程经常会遇到一个问题,如何停止这个Thread?在WPF中提供了Abort方法,但MSDN却告诉我们:

线程不一定会立即中止,或者根本不中止。 如果线程在作为中止过程的一部分被调用的 finally 块中做非常大量的计算,从而无限期延迟中止操作,则会发生这种情况。

先看下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public partial class MainWindow : Window
{
Thread thread;
public MainWindow()
{
InitializeComponent();
Test t = new Test();
thread = new Thread(new ThreadStart(t.Run));
thread.Start();

}

private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("abort");
thread.Abort();
}
}

public class Test
{
public void Run()
{
while (true)
{
Console.WriteLine("Running...");
}
}
}

在ControlTemplate中使用VisualTreeHelper和LogicalTreeHelper

在TabControl中给TabItem定义如下的Template:

1
2
3
4
5
6
7
8
9
10
11
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Border x:Name="mainBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Background="{TemplateBinding Background}" Margin="0">
<Border x:Name="innerBorder" BorderBrush="{StaticResource TabItem.Selected.Border}" BorderThickness="1,1,1,0" Background="{StaticResource TabItem.Selected.Background}" Margin="-1" Opacity="0"/>
</Border>
<DockPanel Margin="2,0">
<Button Click="Button_Click" DockPanel.Dock="Right" Content="X" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="Blue"/>
<ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</DockPanel>
</Grid>
</ControlTemplate>

实现的效果是给每一个TabItem添加关闭按钮:

现在需要实现关闭按钮的功能,那么则需要在后台Button_Click事件中通过sender找到类型为TabItem的parent。