nonocast

Digital Life

Browsing Posts tagged WPF

该有的都有了,包括了WM_*和参数Enum
Win32.cs
鼠标键盘模拟操作
Simulation.cs
鼠标键盘Hook
MouseKeyboardHook.dll

BItmapImage默认情况下是不会进行Preload,之前google竟然也没有什么办法,本来考虑尝试一下thumbnail,隔天再想这个问题突然灵光一闪,Yes,MemoryStream。

<MediaElement x:Name=”video” MediaEnded=”video_MediaEnded” Stretch=”Fill” LoadedBehavior=”Manual” />
增加MediaEnded事件响应,事件响应代码如下:

private void video_MediaEnded(object sender, RoutedEventArgs e) {
	this.video.Position = TimeSpan.FromSeconds(0);
	this.video.Play();
}

关键是需要重置video的Position。

原先Winform只需要在Toolbox中choose加入Shockwave Flash Object即可,可到了WPF则变得有些麻烦,需要通过Winform来做跳板,具体如下:
1. 通过原先Winfrom的引入方式,将Flash控件拖入Winform中,VS会自动生成下面2个dll:

  • AxInterop.ShockwaveFlashObjects.dll
  • Interop.ShockwaveFlashObjects.dll

2. 将这两个dll copy到wpf的project中,在project新建2个目录分别是Lib和Runtime,将Interop…dll放入Lib,Ax…dll放入Runtime,在project编写Post Build Event,如下:
copy “$(ProjectDir)Runtime\*.dll” “$(TargetDir)”
将AxInterop.ShockwaveFlashObjects.dll在build后copy到最终exe生成的目录,因为运行exe需要它。

3. 在wpf project中add reference:

  • AxInterop.ShockwaveFlashObjects.dll
  • Interop.ShockwaveFlashObjects.dll
  • System.Windows.Forms
  • WindowsFormsIntegration

4. 开始编写放入flash的代码:

private void Window_Loaded(object sender, RoutedEventArgs e) {
	System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
	flash = new AxShockwaveFlashObjects.AxShockwaveFlash();
	flash.FSCommand += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEventHandler(flash_FSCommand);
	host.Child = flash;
	this.Stage.Children.Add(host);

	flash.Movie = @"c:\users\nonocast\desktop\hello.swf";
	flash.Play();
	flash.BackgroundColor = 0x000000;
	flash.SetVariable("testValue", "Hello World");
}

private void flash_FSCommand(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent e) {
	string cmd = e.command.ToString();
	MessageBox.Show(cmd);
}

private AxShockwaveFlashObjects.AxShockwaveFlash flash;

以Flash8 AS2为例(:(我也没办法),可以通过FsCommand事件接收flash中fscommand的调用,反之可以通过SetVariable给flash ’send message’.

5. 上面说到的Flash很简单,在第一帧中放入一个Label,然后F9编写’动作’,如下:

var testValue:String = "";
 watchCallback = function (id, oldval, newval):String {
	mylabel.text = newval;
	fscommand("GotIT");
	return newval;
 };
 _root.watch("testValue", watchCallback);

另外一种情况就是,直接采用动态文本,然后在’变量’中填上相应的变量名,然后直接就可以在.NET中Set,这个非常方便。
6. 参考Url:
Using the External API for Flash–JavaScript Communication
如何在 WPF 中嵌入 Flash (ActiveX)
7. 附上我的代码,有点乱糟糟,

WPF采用DispatcherObject作为UI对象的Base Class,DispactcherObject继承自Object,那什么是DispatcherObject以及DispatcherObject和我们又有什么关系呢?
MSDN对DispatcherObject给出的Remark是这样的:

Only the thread that the Dispatcher was created on may access the DispatcherObject directly. To access a DispatcherObject from a thread other than the thread the DispatcherObject was created on, call Invoke or BeginInvoke on the Dispatcher the DispatcherObject is associated with.

这次我们克制一下,不去挖Source Code,从黑盒的角度来说,Dispatcher是一个ThreadLocal的优先级消息队列,通过消息机制可以使得对象内部数据做到Thread-Safe。

  • Thread+Message=ActiveObject

我们通过简单代码来DIY一个具备’消息队列’能力的ActiveObject,

public class DispatcherWorker : DispatcherObject, IDisposable {
	public void DoWork() {
		Console.Write("BEGIN");
		Thread.Sleep(2000);
		Console.WriteLine(" ... END");
	}

	public static DispatcherWorker Create() {
		DispatcherWorker result = null;
		AutoResetEvent e = new AutoResetEvent(false);
		new Thread(new ThreadStart(() => {
			result = new DispatcherWorker();
			e.Set();
			Dispatcher.Run();
		})).Start();
		e.WaitOne();
		return result;
	}

	public void Dispose() {
		this.Dispatcher.InvokeShutdown();
	}
	public void Close() { Dispose(); }
}

然后就可以在任意线程中对这个对象进行Invoke/BeginInvoke DoWork而确保DoWork的完整性。
Console的输出能确保是BEGIN…END BEGIN…END BEGIN…END,而不会是BEGIN BEGIN BEGIN…END…END…END

最后把DispatcherWorker做成一个Template,

public class ActiveObject< T > : DispatcherObject, IDisposable where T : new() {
	protected ActiveObject() { }

	public static T Create() {
		T result = default(T);
		AutoResetEvent e = new AutoResetEvent(false);
		new Thread(new ThreadStart(() => {
			result = new T();
			e.Set();
			Dispatcher.Run();
		})).Start();
		e.WaitOne();
		return result;
	}

	public virtual void Invoke(Action method) {
		this.Dispatcher.Invoke(method);
	}

	public virtual void BeginInvoke(Action method) {
		this.Dispatcher.BeginInvoke(method);
	}

	public virtual void Dispose() {
		this.Dispatcher.InvokeShutdown();
	}

	public virtual void Close() { Dispose(); }
}

Applying Template,

public class MyWorker : ActiveObject< MyWorker > {
	public void DoWork() {
		Console.Write("BEGIN");
		Thread.Sleep(2000);
		Console.WriteLine(" ... END");
	}
}

At last,

MyWorker worker = MyWorker.Create();
for (int i = 0; i < 5; ++i) {
	new Thread(new ThreadStart(() => {
		worker.BeginInvoke(() => worker.DoWork());
	})).Start();
}

That’s all. Hope it helps!
Download SourceCode: ActiveObject.7z

WPF不仅可以通过DoubleAnimation完成数值变化,同时,通过PathAnimation可以完成更复杂的动画效果。

WPF提供以下类来支持PathAnimation:

  • DoubleAnimationUsingPath
  • MatrixAnimationUsingPath
  • PointAnimationUsingPath

具体可以参看MSDN,

下面给出一个简单的example:

代码这里 (ENV: .NET 4.0/VS2010)

由于需要对程序输出做几何校正,所以需要对一些方案进行比较,包括

  • WPF 3D
  • XNA
  • Managed DirectX
  • Unmanaged DirectX

先看一下WPF 3D,步骤和传统的DirectX开发步骤相似,代码会简洁一些,以下图为例,

Xaml如下

由Viewport3D开始,然后需要简单的一下设置摄像机和灯光,模型由MeshGeometry3D负责定义,可以在Xaml中直接定义,也可以在后台中用代码生成对象再加入Viewport3D中。

MeshGeometry3D mesh = new MeshGeometry3D();
List<Point3D> points = new List<Point3D>();
points.Add(new Point3D(0, 2, 0));
points.Add(new Point3D(1, 2, 0.5));
points.Add(new Point3D(2, 2, 1));
points.Add(new Point3D(0, 1, 0));
points.Add(new Point3D(1, 1, 0.5));
points.Add(new Point3D(2, 1, 1));
points.Add(new Point3D(0, 0, 0));
points.Add(new Point3D(1, 0, 0.5));
points.Add(new Point3D(2, 0, 1));

foreach (var each in points) {
	mesh.Positions.Add(each);
}

mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(4);

mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(5);
mesh.TriangleIndices.Add(2);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(5);

mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(7);
mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(6);
mesh.TriangleIndices.Add(7);

mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(8);
mesh.TriangleIndices.Add(5);
mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(7);
mesh.TriangleIndices.Add(8);

VisualBrush brush = new VisualBrush(new MediaElement() { Source = new Uri(@"D:\4Video\Sample\Coral_Reef_Adventure_1080.wmv") });
Material material = new DiffuseMaterial(brush);

List<Point3D> ps = new List<Point3D>();
ps.Add(new Point(0, 0));
ps.Add(new Point(.5, 0));
ps.Add(new Point(1, 0));
ps.Add(new Point(0, .5));
ps.Add(new Point(.5, .5));
ps.Add(new Point(1, .5));
ps.Add(new Point(0, 1));
ps.Add(new Point(0.5, 1));
ps.Add(new Point(1, 1));

mesh.TextureCoordinates = new PointCollection(ps);

ModelVisual3D model = new ModelVisual3D();
model.Content = new GeometryModel3D(mesh, material);
mGeometry = model.Content as GeometryModel3D;
model.Content.Transform = new Transform3DGroup();

this.mainViewport.Children.Add(model);

上面这段代码就是一个生成模型的例子,分为以下几个步骤:
1. 创建MeshGeometry3D对象
2. 向Mesh中添加需要用到的顶点坐标(Point3D),这里通过9个点构成一个2×2的网格,共8个三角形
3. 向Mesh中声明三角形的顶点序列,3个一组,表示一个3角色,考虑DirectX采用右手坐标系,所以三角形坐标必须逆时针顺序,当然对于故意需要朝后的facet则采用顺时针顺序
4. 创建贴图,这里采用VisualBrush,将视频贴到Mesh上
5. 为了使贴图和Mesh建立对应关系,就必须指定顶点与贴图的对应关系,需要指定TextureCoordinates
6. 通过ModelVisual3D包装Mesh送进Viewport中即可

最后给出上面的源代码,这里

ENV: .NET 4,VS2010

WPF中对Image裁剪可以利用Crop属性,以下图为例,
如果需要裁剪以下效果,

可以这样写:

如果需要矩形的裁剪,使用RectangleGeometry就行。

如果需要华丽的裁剪可以试试下面的写法

<PathGeometry Figures=”M 400,0 C 100,60 200,250 0,400 H600 V0″ />

相应的效果如下图:

其中使用了类似LOGO语言的Script,细节可以参考msdn&csdn,

PS.MeidaElement也是OK的

Bezier曲线是一种spline,是一种”用光滑连续函数逼近离散数据”的曲线。三次Bezier曲线由四个点定义,这些点可以被标示为p0,p1,p2,p3。曲线从p0开始,结束于p3。p1,p2称为控制点,通过调整控制点来达到你想要的曲线。

用WPF来实现bezier的显示和控制需要以下几步:

1. 采用BezierSegment显示Bezier曲线

<Path Stroke="DarkGray" StrokeThickness="3">
  <Path.Data>
    <PathGeometry>
      <PathGeometry.Figures>
        <PathFigure StartPoint="100,50">
          <BezierSegment Point1="300,50" Point2="50,300" Point3="400, 400"/>
        </PathFigure>
      </PathGeometry.Figures>
   </PathGeometry>
 </Path.Data>
</Path>

2. 增加控制点

public class BezierPoint : FrameworkElement { }
  • 增加3个依赖属性,包括Fill,Stroke,Center
  • 增加支持拖动的代码

具体实现参考附件中BezierPoint.cs
3. 将控制点绑定到曲线上

<Canvas>
  <my:BezierPoint x:Name="ptStart" Fill="DarkOrange" Stroke="{x:Null}" Canvas.Left="304" Canvas.Top="50" Canvas.ZIndex="10" />
  <my:BezierPoint x:Name="ptCtrl2" Stroke="SkyBlue"  Canvas.Left="124" Canvas.Top="320" Canvas.ZIndex="11" />
  <my:BezierPoint x:Name="ptCtrl1" Stroke="DarkOrange" Canvas.Left="658" Canvas.Top="98" Canvas.ZIndex="12" />
  <my:BezierPoint x:Name="ptEnd" Fill="SkyBlue" Stroke="{x:Null}" Canvas.Left="504" Canvas.Top="380" Canvas.ZIndex="13" />

  <Path Stroke="DarkGray" StrokeThickness="3">
    <Path.Data>
      <PathGeometry>
        <PathGeometry.Figures>
          <PathFigure StartPoint="{Binding ElementName=ptStart,Path=Center}">
            <BezierSegment Point1="{Binding ElementName=ptCtrl1,Path=Center}"
                           Point2="{Binding ElementName=ptCtrl2,Path=Center}"
                           Point3="{Binding ElementName=ptEnd,Path=Center}"/>
          </PathFigure>
        </PathGeometry.Figures>
      </PathGeometry>
    </Path.Data>
  </Path>
</Canvas>

OVER.代码这里下载,
ENV:.NET4.0/VS2010


我的第一个WPF的Window,

  • 自定义窗口边框,支持drag & resize
  • 定义Button Style
  • 定义ListBox Template & Trigger
  • 自定义ScrollBar
  • 使用Border