天天看點

Silverlight InkPresenter 實作路徑回放的探索

從Silverlight從1.0版本開始,就提供了InkPresenter控件。很多人在第一次了解到這個控件時非常興奮,紛紛打算做“手寫識别”“網絡共享白闆”等應用程式。可惜Silverlight的InkPresenter隻是WPF中InkPresenter的閹割版,想要擴充它不是說不可以,而是一件相當傷腦筋的麻煩事——至少,到目前為止,我都沒有見到過真正的Silverlight網絡共享白闆。

webabcd的在4個月前的一篇文章的評論中,也說到了打算做Silverlight 網絡白闆的問題。

而WXWinter(冬)在3個月前用Silverlight+WCF完成了一個“近似”的“網絡共享白闆”。

為什麼我說他是“近似”的呢?

這得先從InkPresenter的原理說起:使用者用滑鼠畫在InkPresenter上的筆迹,都被儲存為

StrokeCollection類型的inkPresenter.Strokes裡。顧名思義,StrokeCollection是Stroke      

的集合。Stroke可以通俗地了解成“一筆”。而這“一筆”是“一條線”,它是包由很多“點”構成的,Stroke把關鍵的點(有轉折的點)儲存在Stroke.StylusPoints裡,StylusPoint則是具體點每個點。

可惜的是,StylusPoint裡除了X和Y坐标外,幾乎沒有提供任何可供程式設計的接口和方法,連Visible這種屬性都沒有提供。Stroke稍好一些,但也提供得不多。

WXWinter(冬)的“網絡共享白闆”是以Stroke為機關的。當使用者畫完“一整筆”後,Silverlight程式将描述這”一整筆”的Stroke通過WCF發送到伺服器,同時通過Timer定時取得伺服器上最新版本的所有Stroke。

用過基于socket的“網絡白闆”的人都知道,WXWinter(冬)的方法隻是一個近似的方法。第一,它沒有真正的“實時”,這個問題不大,就算是不直接使用socket,Shareach也示範了使用WCF的解決方式;第二,它的資料是以“線”為機關的,在實際使用時,對方看不到你畫線的過程,隻是會突然發現自己的螢幕上出現一條别人畫的線,這是一件比較囧的事。

說了這麼多,終于進入正題了:我最近一直在思考以上提到的第二個問題,如何直播或回放使用者畫線的過程,而不是讓那些筆迹一整條一整條地跳出來呢?我認為,首先要把“點”從“線”中分離出來,對“點”程式設計而不是操作“線”;其次記錄使用者畫每個點的準确時間;第三,使用動畫。本文展示一個回放使用者在InkPresenter上塗鴉過程的Demo。

效果圖:

Silverlight InkPresenter 實作路徑回放的探索

現場Demo  (需要安裝Silverlight 3.0控件,在這裡安裝:http://download.microsoft.com/download/0/D/7/0D76C405-E0E5-43CC-89D3-18243A4FCA86/Silverlight.3.0_Developer.exe )

【使用說明】

1.等待資料加載完,

2.點選“開始錄制”,

3.音樂響起,你可以随便塗寫.

4.畫完後點選“停止錄制”.

5.點選”回放預覽”檢視你的傑作。

(如果你看不到下面的Silverlight對象,可以到這裡檢視:http://azuredrive.blob.core.windows.net/netdrive1/file_98a1cf05-94e2-4918-a6ef-29e791c8e327.html)

Silverlight InkPresenter 實作路徑回放的探索

實作步驟草圖:

1.InkPresenter的XAML代碼及基本操作

Silverlight InkPresenter 實作路徑回放的探索

  <InkPresenter Name='inkPresenter' Canvas.Left='10' Canvas.Top='10' 

Silverlight InkPresenter 實作路徑回放的探索

                MouseLeftButtonDown='onInkPresenterDown' MouseMove='onInkPresenterMove' MouseLeftButtonUp='onInkPresenterUp'>

Silverlight InkPresenter 實作路徑回放的探索

        <InkPresenter.Resources>

Silverlight InkPresenter 實作路徑回放的探索

            <Storyboard Duration="0:0:0" Completed="onStrokePlaybackTimerTick" x:Name="strokePlaybackTimer" />

Silverlight InkPresenter 實作路徑回放的探索

        </InkPresenter.Resources>

Silverlight InkPresenter 實作路徑回放的探索

        <MediaElement Name='mediaElement' Source="http://azuredrive.blob.core.windows.net/netdrive1/file_c6184705-b9f7-49e9-a2e9-1e76a01a4565.wmv" Width='720' Height='480' 

Silverlight InkPresenter 實作路徑回放的探索

                  AutoPlay='False'   MediaEnded="onMediaEnded"/>

Silverlight InkPresenter 實作路徑回放的探索

    </InkPresenter>

InkPresenter的基本操作請參考webabcd的這篇文章。

2.用視訊(或音頻)的時間軸來作為塗鴉事件的時間軸,記錄每一筆的開始時間

仔細看看上文的InkPresenter的XAML代碼。它的Resources裡是動畫資訊,它的内容僅僅是一個WMV媒體檔案。我們之前提到了要儲存每一個筆畫的時間,就可以直接使用這個媒體檔案的時間軸。

具體操作是這樣的:

Silverlight InkPresenter 實作路徑回放的探索

   if (isRecording)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

{

Silverlight InkPresenter 實作路徑回放的探索

                //捕獲滑鼠

Silverlight InkPresenter 實作路徑回放的探索

                inkPresenter.CaptureMouse();

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                newStroke = new Stroke();

Silverlight InkPresenter 實作路徑回放的探索

                //設定該筆畫的屬性。本Demo中全部使用預設屬性。

Silverlight InkPresenter 實作路徑回放的探索

                newStroke.DrawingAttributes = defaultDrawingAttributes;

Silverlight InkPresenter 實作路徑回放的探索

                //記錄該筆的第一個點的資訊

Silverlight InkPresenter 實作路徑回放的探索

                newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkPresenter));

Silverlight InkPresenter 實作路徑回放的探索

                inkPresenter.Strokes.Add(newStroke);

Silverlight InkPresenter 實作路徑回放的探索

                //記錄該筆第一個點畫下的時間

Silverlight InkPresenter 實作路徑回放的探索

                strokeStartTimes.Add(mediaElement.Position.Seconds);

Silverlight InkPresenter 實作路徑回放的探索

            }

Silverlight InkPresenter 實作路徑回放的探索

  //用于儲存每筆每個點的資訊,分别用';'和','隔開

Silverlight InkPresenter 實作路徑回放的探索

        string inkStringForPlayback = null;

Silverlight InkPresenter 實作路徑回放的探索

        //用于儲存每筆的開始時間

Silverlight InkPresenter 實作路徑回放的探索

        List<int> strokeStartTimes = new List<int>();

Silverlight InkPresenter 實作路徑回放的探索

private string ConvertInkToString(StrokeCollection strokes)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            string serializedStylusPoints = "";

Silverlight InkPresenter 實作路徑回放的探索

            if (strokes != null)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                int strokeCount = strokes.Count;

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                for (int i = 0; i < strokeCount; i++)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    Stroke stroke = strokes[i];

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    int packetCount = stroke.StylusPoints.Count;

Silverlight InkPresenter 實作路徑回放的探索

                    for (int j = 0; j < packetCount; j++)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                        StylusPoint stylusPoint = stroke.StylusPoints[j];

Silverlight InkPresenter 實作路徑回放的探索

                        serializedStylusPoints += stylusPoint.X.ToString();

Silverlight InkPresenter 實作路徑回放的探索

                        serializedStylusPoints += ",";

Silverlight InkPresenter 實作路徑回放的探索

                        serializedStylusPoints += stylusPoint.Y.ToString();

Silverlight InkPresenter 實作路徑回放的探索

                        if (j != packetCount - 1)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                            serializedStylusPoints += ",";

Silverlight InkPresenter 實作路徑回放的探索

                        }

Silverlight InkPresenter 實作路徑回放的探索

                        else

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                            serializedStylusPoints += ";";

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    }

Silverlight InkPresenter 實作路徑回放的探索

                }

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            return serializedStylusPoints;

Silverlight InkPresenter 實作路徑回放的探索

        }

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

        private StrokeCollection ConvertStringToInk(string serializedStylusPoints)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            StrokeCollection strokes = new StrokeCollection();

Silverlight InkPresenter 實作路徑回放的探索

            string[] strokeStrings = serializedStylusPoints.Split(';');

Silverlight InkPresenter 實作路徑回放的探索

            for (var i = 0; i < strokeStrings.Length - 1; i++)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                Stroke stroke = new Stroke();

Silverlight InkPresenter 實作路徑回放的探索

                stroke.DrawingAttributes = defaultDrawingAttributes;

Silverlight InkPresenter 實作路徑回放的探索

                string[] stylusPoints = strokeStrings[i].Split(',');

Silverlight InkPresenter 實作路徑回放的探索

                for (var j = 0; j < stylusPoints.Length / 2; j++)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    StylusPoint stylusPoint = new StylusPoint();

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    stylusPoint.X = double.Parse(stylusPoints[2 * j]);

Silverlight InkPresenter 實作路徑回放的探索

                    stylusPoint.Y = double.Parse(stylusPoints[2 * j + 1]);

Silverlight InkPresenter 實作路徑回放的探索

                    stroke.StylusPoints.Add(stylusPoint);

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                strokes.Add(stroke);

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            return strokes;

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

private void onStrokePlaybackTimerTick(object sender,  EventArgs e)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            if (strokesForPlayback.Count == 0) return;

Silverlight InkPresenter 實作路徑回放的探索

            Stroke currentStroke = strokesForPlayback[playbackStrokeIndex];

Silverlight InkPresenter 實作路徑回放的探索

            if (playbackPointIndex == 0)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                if (mediaElement.Position.Seconds < strokeStartTimes[playbackStrokeIndex])

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                    strokePlaybackTimer.Begin();

Silverlight InkPresenter 實作路徑回放的探索

                    return;

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                strokeToPlayback = new Stroke();

Silverlight InkPresenter 實作路徑回放的探索

                inkPresenter.Strokes.Add(strokeToPlayback);

Silverlight InkPresenter 實作路徑回放的探索

                strokeToPlayback.DrawingAttributes = currentStroke.DrawingAttributes;

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            strokeToPlayback.StylusPoints.Add(currentStroke.StylusPoints[playbackPointIndex]);

Silverlight InkPresenter 實作路徑回放的探索

            playbackPointIndex++;

Silverlight InkPresenter 實作路徑回放的探索

            if (playbackPointIndex < currentStroke.StylusPoints.Count)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                strokeToPlayback.StylusPoints.Add(currentStroke.StylusPoints[playbackPointIndex]);

Silverlight InkPresenter 實作路徑回放的探索

                playbackPointIndex++;

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            if (playbackPointIndex == currentStroke.StylusPoints.Count)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

                playbackPointIndex = 0;

Silverlight InkPresenter 實作路徑回放的探索

                playbackStrokeIndex++;

Silverlight InkPresenter 實作路徑回放的探索

                if (playbackStrokeIndex == strokesForPlayback.Count)

Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索
Silverlight InkPresenter 實作路徑回放的探索

            strokePlaybackTimer.Begin();

Silverlight InkPresenter 實作路徑回放的探索

繼續閱讀