WPF纯手工两步打造图片切割工具(二)
上一节已经完成了功能需求和界面布局,这一节就说明一下编码。
本文分两部分:
(一)界面布局及数据初始化
(二)编码实现
1、 既然要求支持批量处理,那么一次就应该允许选择多个文件,在上一节的最后已经说明了一次选择多个文件,OpenFileDialog在返回的时候通过FileNames得到的是一个数组,所以要进行简单的转换成用分号(;)分隔的字符串,以便填入文本框中。
(一)界面布局及数据初始化
(二)编码实现
1、 既然要求支持批量处理,那么一次就应该允许选择多个文件,在上一节的最后已经说明了一次选择多个文件,OpenFileDialog在返回的时候通过FileNames得到的是一个数组,所以要进行简单的转换成用分号(;)分隔的字符串,以便填入文本框中。
1 sorImage =null;
2 System.Windows.Forms.OpenFileDialog ofd =new System.Windows.Forms.OpenFileDialog();
3 ofd.Multiselect =true;
4 ofd.Filter ="图片文件(*.jpg *.gif *.png)|*.jpg;*.gif;*.png|All Files (*.*)|*.*";
5 if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
6 {
7 sorImage = ofd.FileNames;
8 }
9 else
10 sorImage =null;
11 if (sorImage !=null)
12 {
13 StringBuilder sb =new StringBuilder();
14 foreach (string item in sorImage)
15 {
16 sb.Append(item +";");
17 }
18 this.txtSource.Text = sb.ToString();
19 this.ucProgressBar1.Total = sorImage.Length;
20 this.ucProgressBar1.Value =0;
21 this.spnlView.Children.Clear();
22 }
2 System.Windows.Forms.OpenFileDialog ofd =new System.Windows.Forms.OpenFileDialog();
3 ofd.Multiselect =true;
4 ofd.Filter ="图片文件(*.jpg *.gif *.png)|*.jpg;*.gif;*.png|All Files (*.*)|*.*";
5 if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
6 {
7 sorImage = ofd.FileNames;
8 }
9 else
10 sorImage =null;
11 if (sorImage !=null)
12 {
13 StringBuilder sb =new StringBuilder();
14 foreach (string item in sorImage)
15 {
16 sb.Append(item +";");
17 }
18 this.txtSource.Text = sb.ToString();
19 this.ucProgressBar1.Total = sorImage.Length;
20 this.ucProgressBar1.Value =0;
21 this.spnlView.Children.Clear();
22 }
同时,为了方便处理过程中的遍历,使用了Queue保存已经选择的图片文件,这样每次取出来个文件来处理就行了,直到Queue中不再有元素。
1 Queue<string> queue =new Queue<string>();
2、 在处理过程中,有一个动画处理每一张要处理的图片,这张图片初始的时候是铺满整个窗体,并且是完全透明,然后随着动画的进行,透明度逐渐增加到1,并且伴随着其他效果。整个动画使用了一个Storyboard和四个DoubleAnimation,分别为:
1 Storyboard sbSize =new Storyboard();
privatevoid InitStoryboard()
{
#region 大小
//宽
DoubleAnimation daWidth =new DoubleAnimation()
{
From =1,
To =0,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daWidth);
Storyboard.SetTarget(daWidth, this.imgMain);
Storyboard.SetTargetProperty(daWidth, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
//高
DoubleAnimation daHeight =new DoubleAnimation()
{
From =1,
To =0,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daHeight);
Storyboard.SetTarget(daHeight, this.imgMain);
Storyboard.SetTargetProperty(daHeight, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
#endregion
#region 移动
//移动
DoubleAnimation daTop =new DoubleAnimation()
{
To =280,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daTop);
Storyboard.SetTarget(daTop, this.imgMain);
Storyboard.SetTargetProperty(daTop, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[2].(TranslateTransform.Y)"));
#endregion
#region 透明度
DoubleAnimation daOpacity =new DoubleAnimation()
{
From =0,
To =1,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daOpacity);
Storyboard.SetTarget(daOpacity, this.imgMain);
Storyboard.SetTargetProperty(daOpacity, new PropertyPath(Image.OpacityProperty));
#endregion
}
{
#region 大小
//宽
DoubleAnimation daWidth =new DoubleAnimation()
{
From =1,
To =0,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daWidth);
Storyboard.SetTarget(daWidth, this.imgMain);
Storyboard.SetTargetProperty(daWidth, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
//高
DoubleAnimation daHeight =new DoubleAnimation()
{
From =1,
To =0,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daHeight);
Storyboard.SetTarget(daHeight, this.imgMain);
Storyboard.SetTargetProperty(daHeight, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
#endregion
#region 移动
//移动
DoubleAnimation daTop =new DoubleAnimation()
{
To =280,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daTop);
Storyboard.SetTarget(daTop, this.imgMain);
Storyboard.SetTargetProperty(daTop, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[2].(TranslateTransform.Y)"));
#endregion
#region 透明度
DoubleAnimation daOpacity =new DoubleAnimation()
{
From =0,
To =1,
Duration =new Duration(TimeSpan.FromSeconds(1)),
FillBehavior = FillBehavior.Stop
};
sbSize.Children.Add(daOpacity);
Storyboard.SetTarget(daOpacity, this.imgMain);
Storyboard.SetTargetProperty(daOpacity, new PropertyPath(Image.OpacityProperty));
#endregion
}
这样形成动画效果叠加,展示了一个从无到有,从大到小,从上到下的渐变过程。
3、 点击启动后,先检查必要的参数及选项输入,把选中的文件一个一个保存的Queue中。接着就是开始启动动画了。
1 queue.Clear();
2 foreach (string item in sorImage)
3 {
4 queue.Enqueue(item);
5 }
6
7 this.sbSize.Begin();
2 foreach (string item in sorImage)
3 {
4 queue.Enqueue(item);
5 }
6
7 this.sbSize.Begin();
4、 动画执行完成后,进行图片切割,然后把把切割后的缩略图加载到窗体下方的列表中。为了显示效果,在图片列表中缩略图上做了一些效果,鼠标移上/移出,点击等。
1 void Do(object obj)
2 {
3 DoParameter para = obj as DoParameter;
4 if (File.Exists(currentImgUrl))
5 {
6 System.Windows.Forms.Application.DoEvents();
7 this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) =>
8 {
9 BitmapImage im = ImageHelper.Thumbnail(currentImgUrl,
10 System.IO.Path.Combine(para.DestinationPath, System.IO.Path.GetFileName(currentImgUrl)),
11 para.IsRename,
12 para.Width,
13 para.Height,
14 para.Mode,
15 para.Interpolation,
16 para.Smoothing,
17 para.Watermark);
18
19 this.ucProgressBar1.Value +=1;
20
21 Image imgList =new Image();
22 imgList.SetValue(Image.SourceProperty, im);
23 imgList.Height =90;
24 imgList.Width =120;// (4.0 / 3) * 90;
25 imgList.Margin =new Thickness(5, 0, 5, 0);
26 DropShadowEffect d =new DropShadowEffect();
27 d.BlurRadius =3;
28 d.ShadowDepth =4;
29 d.Color = Color.FromRgb(218, 214, 114);
30 imgList.Effect = d;
31 imgList.MouseEnter +=new System.Windows.Input.MouseEventHandler(imgList_MouseEnter);
32 imgList.MouseLeave +=new System.Windows.Input.MouseEventHandler(imgList_MouseLeave);
33 this.spnlView.Children.Add(imgList);
34 im =null;
35 }), null, null);
36 currentImg =null;
37 }
38 }
39
40 void imgList_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
41 {
42 Image img = (Image)sender;
43 DropShadowEffect d =new DropShadowEffect();
44 d.BlurRadius =3;
45 d.ShadowDepth =4;
46 d.Color = Color.FromRgb(218, 214, 114);
47 img.Effect = d;
48 }
49
50 void imgList_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
51 {
52 Image img = (Image)sender;
53 DropShadowEffect d =new DropShadowEffect();
54 d.BlurRadius =3;
55 d.ShadowDepth =4;
56 d.Color = Color.FromRgb(255, 0, 0);
57 img.Effect = d;
58 this.imgBG.ImageSource = img.Source;
59 }
2 {
3 DoParameter para = obj as DoParameter;
4 if (File.Exists(currentImgUrl))
5 {
6 System.Windows.Forms.Application.DoEvents();
7 this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) =>
8 {
9 BitmapImage im = ImageHelper.Thumbnail(currentImgUrl,
10 System.IO.Path.Combine(para.DestinationPath, System.IO.Path.GetFileName(currentImgUrl)),
11 para.IsRename,
12 para.Width,
13 para.Height,
14 para.Mode,
15 para.Interpolation,
16 para.Smoothing,
17 para.Watermark);
18
19 this.ucProgressBar1.Value +=1;
20
21 Image imgList =new Image();
22 imgList.SetValue(Image.SourceProperty, im);
23 imgList.Height =90;
24 imgList.Width =120;// (4.0 / 3) * 90;
25 imgList.Margin =new Thickness(5, 0, 5, 0);
26 DropShadowEffect d =new DropShadowEffect();
27 d.BlurRadius =3;
28 d.ShadowDepth =4;
29 d.Color = Color.FromRgb(218, 214, 114);
30 imgList.Effect = d;
31 imgList.MouseEnter +=new System.Windows.Input.MouseEventHandler(imgList_MouseEnter);
32 imgList.MouseLeave +=new System.Windows.Input.MouseEventHandler(imgList_MouseLeave);
33 this.spnlView.Children.Add(imgList);
34 im =null;
35 }), null, null);
36 currentImg =null;
37 }
38 }
39
40 void imgList_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
41 {
42 Image img = (Image)sender;
43 DropShadowEffect d =new DropShadowEffect();
44 d.BlurRadius =3;
45 d.ShadowDepth =4;
46 d.Color = Color.FromRgb(218, 214, 114);
47 img.Effect = d;
48 }
49
50 void imgList_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
51 {
52 Image img = (Image)sender;
53 DropShadowEffect d =new DropShadowEffect();
54 d.BlurRadius =3;
55 d.ShadowDepth =4;
56 d.Color = Color.FromRgb(255, 0, 0);
57 img.Effect = d;
58 this.imgBG.ImageSource = img.Source;
59 }
5、要注意一点,当鼠标离开缩略图列表区域时,还得把窗体背景重新设置为默认图片。
1 privatevoid scrolls_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
2 {
3 if (this.imgBG.ImageSource != bgImg)
4 this.imgBG.ImageSource = bgImg;
5 }
2 {
3 if (this.imgBG.ImageSource != bgImg)
4 this.imgBG.ImageSource = bgImg;
5 }
6、 一图片处理完成后,紧接着检查Queue中是否还有元素,如果有则继续执行动画,如此循环4和5两步,否则退出此次执行命令。
View Code
1 void InitImageMain()
2 {
3 if (queue.Count >0&& isRunning)
4 {
5 currentImgUrl = queue.Dequeue();
6 currentImg = ImageHelper.CreateBitmapImage(currentImgUrl); ;
7 this.imgMain.Source = currentImg;
8 currentImg =null;
9
10 this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) =>
11 {
12 sbSize.Begin();
13 }), null, null);
14 }
15 else
16 {
17 this.imgMain.Source =null;
18 isRunning =false;
19 this.btnStart.IsEnabled =true;
20 }
21 }
2 {
3 if (queue.Count >0&& isRunning)
4 {
5 currentImgUrl = queue.Dequeue();
6 currentImg = ImageHelper.CreateBitmapImage(currentImgUrl); ;
7 this.imgMain.Source = currentImg;
8 currentImg =null;
9
10 this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new EventHandler((o1, e1) =>
11 {
12 sbSize.Begin();
13 }), null, null);
14 }
15 else
16 {
17 this.imgMain.Source =null;
18 isRunning =false;
19 this.btnStart.IsEnabled =true;
20 }
21 }
7、另外一点,使用的那个进度条是自定义的用户控件,其实是一个简单组合,就是在一个ProgressBar上放一个TextBlock即可,这样方便控制,既展示了进度,也显示了提示信息。
1 <Grid>
2 <ProgressBar x:Name="pressBar" Margin="0,0,0,0" Opacity="0.7"/>
3 <TextBlock x:Name="txtValue" Margin="0,0,0,0" TextWrapping="Wrap" Text="0/0" TextAlignment="Center" FontSize="16"/>
4 </Grid>
2 <ProgressBar x:Name="pressBar" Margin="0,0,0,0" Opacity="0.7"/>
3 <TextBlock x:Name="txtValue" Margin="0,0,0,0" TextWrapping="Wrap" Text="0/0" TextAlignment="Center" FontSize="16"/>
4 </Grid>
View Code
1 publicpartialclass UcProgressBar : UserControl
2 {
3 public UcProgressBar()
4 {
5 InitializeComponent();
6 }
7
8 int _total =100;
9
10 publicint Total
11 {
12 get { return _total; }
13 set
14 {
15 if (value<0)
16 {
17 value =0;
18 }
19 _total = value;
20 this.pressBar.Maximum =this._total;
21 this.txtValue.Text =string.Format("{0}/{1}", _value, _total);
22 }
23 }
24
25 int _value =0;
26
27 publicint Value
28 {
29 get { return _value; }
30 set
31 {
32 if (value<0)
33 {
34 value =0;
35 }
36 _value = value;
37 this.pressBar.Value = value;
38 this.txtValue.Text =string.Format("{0}/{1}", _value, _total);
39 }
40 }
41 }
2 {
3 public UcProgressBar()
4 {
5 InitializeComponent();
6 }
7
8 int _total =100;
9
10 publicint Total
11 {
12 get { return _total; }
13 set
14 {
15 if (value<0)
16 {
17 value =0;
18 }
19 _total = value;
20 this.pressBar.Maximum =this._total;
21 this.txtValue.Text =string.Format("{0}/{1}", _value, _total);
22 }
23 }
24
25 int _value =0;
26
27 publicint Value
28 {
29 get { return _value; }
30 set
31 {
32 if (value<0)
33 {
34 value =0;
35 }
36 _value = value;
37 this.pressBar.Value = value;
38 this.txtValue.Text =string.Format("{0}/{1}", _value, _total);
39 }
40 }
41 }
总结:小工具的整个实现并不复杂,只是把几个常用的元素融合到了一起:动画、图片切割、自定义用户控件。
本博客文章版权归博客园和solan3000共同所有。