欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

WPF用狀態(tài)模式開發(fā)截圖功能

 更新時間:2025年03月18日 09:36:09   作者:caoruipeng  
狀態(tài)模式是設計模式中的一種行為設計模式,尤其在游戲中開發(fā)中還是比較常見的,本文將通過狀態(tài)模式開發(fā)一個截圖功能,有需要的小伙伴可以參考下

狀態(tài)模式

狀態(tài)模式是設計模式中的一種行為設計模式,對很多人來說,這個模式平時可能用不到。但是如果你做游戲開發(fā)的話,我相信你應該對這個模式有一個很深刻的理解。狀態(tài)模式在游戲中開發(fā)中還是比較常見的。狀態(tài)模式將狀態(tài)的行為封裝在獨立的狀態(tài)類中,使得狀態(tài)轉(zhuǎn)換變得更加清晰和易于管理。這樣的話,對象只負責狀態(tài)的切換,不負責具體的行為。

比如射擊類游戲:玩家模式是原地站立狀態(tài),當用戶按下前進的時候,玩家的狀態(tài)切換為前進狀態(tài),當按下后退的時候,狀態(tài)切換為后退狀態(tài)。當按下鼠標左鍵時,玩家進入射擊狀態(tài)。對于同一個玩家對象來說,不同的狀態(tài)下,都是由不同的行為邏輯。這些邏輯,如果全部都在玩家這個對象類中實現(xiàn)的話,將會非常復雜。狀態(tài)模式,就是用來解決這個問題的。

狀態(tài)模式的主要組成部分包括:

(1)、上下文(Context):維護一個具體狀態(tài)的實例,這個實例定義了當前的狀態(tài)。

(2)、狀態(tài)接口(State):定義了所有具體狀態(tài)類的公共接口。

(3)、 具體狀態(tài)類(Concrete States):實現(xiàn)狀態(tài)接口,并根據(jù)上下文的狀態(tài)改變其行為。

下面用C#簡單的寫一個狀態(tài)模式的代碼,來實現(xiàn)上面的玩家類。

1、定義狀態(tài)接口IPlayerState表示玩家狀態(tài),Handle用于處理不同狀態(tài)下玩家的具體行為

public interface IPlayerState
{
    void Handle(PlayerContext context);
}

2、定義具體狀態(tài)類:默認站立狀態(tài)類:DefaultState 、前進狀態(tài):MoveForwardState 、后退狀態(tài):MoveBackwardState 、射擊狀態(tài):DesignState 、死亡狀態(tài):DeadState

public class DefaultState : IPlayerState
{
    public void Handle(PlayerContext context)
    {
        Console.WriteLine("玩家處于默認站立狀態(tài).");
        // 狀態(tài)轉(zhuǎn)換邏輯
    }
}

public class MoveForwardState : IPlayerState
{
    public void Handle(PlayerContext context)
    {
        Console.WriteLine("玩家正在前進....");
        // 狀態(tài)轉(zhuǎn)換邏輯 這里可以實時渲染玩家位置
    }
}

public class MoveBackwardState : IPlayerState
{
    public void Handle(PlayerContext context)
    {
        Console.WriteLine("玩家正在后退...");
        // 狀態(tài)轉(zhuǎn)換邏輯 這里可以實時渲染玩家位置
    }
}

public class DesignState : IPlayerState
{
    public void Handle(PlayerContext context)
    {
        Console.WriteLine("玩家正在射擊...");
        // 狀態(tài)轉(zhuǎn)換邏輯 這里可以判斷玩家是否擊中對方,更新對方血條和自己血條
    }
}

public class DeadState : IPlayerState
{
    public void Handle(PlayerContext context)
    {
        Console.WriteLine("玩家死亡...");
        // 狀態(tài)轉(zhuǎn)換邏輯 這里可以對本局游戲進行玩家分數(shù)結(jié)算
    }
}

從上面可以看到,不同的狀態(tài),玩家的邏輯被清晰的描述出來。這樣的話,我們就不用在玩家類Player中,通過if-else切換狀態(tài),實現(xiàn)業(yè)務邏輯,結(jié)構(gòu)也會非常清晰。

3、定義上下文類:上下文類中存儲了玩家的當前狀態(tài),并且可以通過上下文切換玩家狀態(tài)。這是狀態(tài)類中最基本的元素,當然還可以包含其他的狀態(tài)數(shù)據(jù),比如玩家的實時坐標,玩家的血條信息等等。

public class PlayerContext
{
    private IPlayerState _state;

    public PlayerContext()
    {
        _state = new DefaultState(); // 初始狀態(tài)為默認狀態(tài)
    }

    public void SetState(IPlayerState state)
    {
        _state = state;
    }

    public void Request()
    {
        _state.Handle(this);
    }
}

4、在對象中,切換狀態(tài),調(diào)用對應的狀態(tài)類邏輯。當然我們只是在這里手動切換狀態(tài) ,實際開發(fā)中,我們一般是監(jiān)聽鼠標鍵盤事件或者鼠標事件之后切換玩家狀態(tài)。

class Program
{
    static void Main(string[] args)
    {
        PlayerContext context = new PlayerContext();

        // 初始狀態(tài)為默認狀態(tài)
        context.Request(); 

        // 切換到前進狀態(tài)
        context.SetState(new MoveForwardState());
        context.Request(); 

        // 切換到后退狀態(tài)
        context.SetState(new MoveBackwardState());
        context.Request();

        // 切換到設計狀態(tài)
        context.SetState(new DesignState());
        context.Request();

        // 切換到死亡狀態(tài)
        context.SetState(new DeadState());
        context.Request(); 
    }
}

基本原理

介紹完狀態(tài)模式的基本遠離之后,接下來介紹截圖功能的基本原理:通過捕獲屏幕上的特定區(qū)域并將其保存為圖像文件,在WPF中,我們可以使用System.Drawing命名空間中的類來實現(xiàn)這一功能。具體步驟如下:

1.    捕獲屏幕區(qū)域:使用Graphics.CopyFromScreen方法從屏幕上復制指定區(qū)域的像素。

2.    保存圖像:將捕獲的像素數(shù)據(jù)保存為位圖(Bitmap)格式。

3.    顯示或處理圖像:將位圖轉(zhuǎn)換為WPF可以處理的BitmapSource格式,以便在界面上顯示或進一步處理。

我們先來看下如何保存圖像:

public static System.Drawing.Bitmap Snapshot(int x, int y, int width, int height)
{
    System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
    using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
    {
        graphics.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height), System.Drawing.CopyPixelOperation.SourceCopy);
    }
    return bitmap;
}

在截圖過程中,我們使用了狀態(tài)模式來管理截圖過程中的不同狀態(tài)。

當我們按下快捷鍵的時候, 處于默認狀態(tài),具體行為是:將一個WPF窗體背景設置為透明,寬高設置為和屏幕大小一致。

當我們按下鼠標左鍵的時候,處理開始截圖狀態(tài)(鼠標左鍵單擊事件),具體行為是:記錄截圖的開始坐標,也就是截圖矩形的左上角坐標。

當按下鼠標開始移動端時候,處于截圖中狀態(tài)(鼠標移動事件),具體行為是:不斷記錄截圖的實時坐標,作為截圖區(qū)域的右下角坐標,同時用戶選中的區(qū)域,背景色要設置成亮色,可以清晰可見。

當放開鼠標的時候,處于截圖結(jié)束狀態(tài)(鼠標左鍵抬起事件),具體行為是:記錄截圖的終止坐標,也就是截圖舉行的右下角坐標。

當下鼠標移動的時候,處于移動中狀態(tài),已經(jīng)選好的截圖區(qū)域是可以移動的,具體行為是:記錄鼠標移動 偏差,動態(tài)設置選中區(qū)域的背景色。

當然還有其他的狀態(tài)類,就不一一描述了。

首先定義我們的狀態(tài)類接口IScreentState :表示截圖狀態(tài)類

public interface IScreentState
{
    void ProcessState(StateContext context);
}

其次定義具體的狀態(tài)實現(xiàn)類:StartState、SelectState、SelectingState、SelectedState、MoveState、MovingState、MovedState、EndState總共有8個狀態(tài)了,這里我只實現(xiàn)了截圖和移動功能,并沒有實現(xiàn)拖拽功能,如果需要拖拽修改截圖區(qū)域大小功能,大家可以自行實現(xiàn)。

public class StartState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        SnapShotWindow win = context._window;
        win.clipRect.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#8000"));
        win.Cursor = Cursors.Arrow;
        win.leftPanel.Width = 0;
        win.topPanel.Height = 0;
        win.rightPanel.Width = 0;
        win.bottomPanel.Height = 0;
    }
}

public class SelectState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        context._startPoint = context._args.GetPosition(context._window);
    }
}

public class SelectingState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        SnapShotWindow win = context._window;
        win.clipRect.Background = Brushes.Transparent;
        context._endPoint = context._args.GetPosition(win);
        win.leftPanel.Width = context._startPoint.X;
        win.topPanel.Height = context._startPoint.Y;
        win.rightPanel.Width = win.ActualWidth - context._endPoint.X;
        win.bottomPanel.Height = win.ActualHeight - context._endPoint.Y;
        win.snapShotInfo.Text = context.GetText();
    }
}

public class SelectedState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        context._endPoint = context._args.GetPosition(context._window);
    }
}

public class MoveState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        SnapShotWindow win = context._window;
        context._mouseDownPosition = context._args.GetPosition(win);
        context._mouseDownMargin = new Thickness(win.leftPanel.ActualWidth, win.topPanel.ActualHeight, win.rightPanel.ActualWidth, win.bottomPanel.Height); ;

        var relativePosition = context._args.GetPosition(win);
        if (relativePosition.X >= 0 && relativePosition.X <= win.clipRect.ActualWidth &&
            relativePosition.Y >= 0 && relativePosition.Y <= win.clipRect.ActualHeight)
        {
            context._allowMove = true;
            win.Cursor = Cursors.SizeAll;
        }
    }
}

public class MovingState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        SnapShotWindow win = context._window;
        win.Cursor = Cursors.SizeAll;
        //todo 隱藏操作按鈕
        var pos = context._args.GetPosition(win);  //拖拽后鼠標的位置
        var dp = pos - context._mouseDownPosition; //鼠標移動的偏移量
        Thickness newThickness = new Thickness(context._mouseDownMargin.Left + dp.X,
            context._mouseDownMargin.Top + dp.Y,
            context._mouseDownMargin.Right - dp.X,
            context._mouseDownMargin.Bottom - dp.Y);
        win.leftPanel.Width = newThickness.Left < 0 ? 0 : newThickness.Left;
        win.topPanel.Height = newThickness.Top < 0 ? 0 : newThickness.Top;
        win.rightPanel.Width = newThickness.Right < 0 ? 0 : newThickness.Right;
        win.bottomPanel.Height = newThickness.Bottom < 0 ? 0 : newThickness.Bottom;
        win.snapShotInfo.Text = context.GetText();
    }
}

public class MovedState : IScreentState
{
    public void ProcessState(StateContext context)
    {
        //todo 顯示操作按鈕
    }
}

public class EndState : IScreentState
{
    public void ProcessState(StateContext context)
    {
    }
}

最后是狀態(tài)上下文類:

public class StateContext
{
    public IScreentState _currentState;

    public SnapShotWindow _window;
    public MouseEventArgs _args;
    //框選開始坐標
    public Point _startPoint;
    //框選結(jié)束坐標
    public Point _endPoint;
    //目前狀態(tài)
    public ScreenState _state;
    //開始拖拽時,鼠標按下的位置
    public Point _mouseDownPosition;
    //開始拖拽時,鼠標按下控件的Margin
    public Thickness _mouseDownMargin;
    public bool _allowMove = false;

    private readonly IScreentState _startState;
    private readonly IScreentState _selectState;
    private readonly IScreentState _selectingState;
    private readonly IScreentState _selectedState;
    private readonly IScreentState _moveState;
    private readonly IScreentState _movingState;
    private readonly IScreentState _movedState;
    private readonly IScreentState _endState;

    public StateContext(SnapShotWindow window)
    {
        _window = window;
        _startState = new StartState();
        _selectState = new SelectState();
        _selectingState = new SelectingState();
        _selectedState = new SelectedState();
        _moveState = new MoveState();
        _movingState = new MovingState();
        _movedState = new MovedState();
        _endState = new EndState();

        _state = ScreenState.Start;
        _currentState = _startState;
        SetNewState(_state);
    }

    public void SetNewState(ScreenState state)
    {
        _state = state;
        switch (state)
        {
            case ScreenState.Start:
                _currentState = _startState;
                break;
            case ScreenState.Select:
                _currentState = _selectState;
                break;
            case ScreenState.Selecting:
                _currentState = _selectingState;
                break;
            case ScreenState.Selected:
                _currentState = _selectedState;
                break;
            case ScreenState.Move:
                _currentState = _moveState;
                break;
            case ScreenState.Moving:
                _currentState = _movingState;
                break;
            case ScreenState.Moved:
                _currentState = _movedState;
                break;
            case ScreenState.End:
                _currentState = _endState;
                break;
        }
        _currentState.ProcessState(this);
    }

    public void SetNewState(ScreenState state, MouseEventArgs args)
    {
        _args = args;
        var point = args.GetPosition(_window);
        SetNewState(state);
    }

    public string GetText(double offsetX = 0, double offsetY = 0)
    {
        Point leftTop = _window.clipRect.PointToScreen(new Point(0, 0));
        Point rightBottom = _window.clipRect.PointToScreen(new Point(_window.clipRect.ActualWidth, _window.clipRect.ActualHeight));

        double width = Math.Round(Math.Abs(rightBottom.X - leftTop.X) + offsetX);
        double height = Math.Round(Math.Abs(rightBottom.Y - leftTop.Y) + offsetY);

        return $"{leftTop}    {width}×{height}";
    }

    public bool IsInClipRect(Point point)
    {
        var relativePosition = point;
        if (relativePosition.X >= 0 && relativePosition.X <= _window.clipRect.ActualWidth &&
            relativePosition.Y >= 0 && relativePosition.Y <= _window.clipRect.ActualHeight)
        {
            return true;
        }
        return false;
    }
}

兩外XAML主界面的布局,就不給大家貼出來,源代碼已經(jīng)上傳到github:https://github.com/caoruipeng123/ScreenApp

運行效果

接下來看下實際的運行效果:進入截圖頁面之后,單擊鼠標坐標開始框選截圖區(qū)域,雙擊鼠標左鍵,可以結(jié)束截圖,并且圖片會設置到操作系統(tǒng)的粘貼板上,你可以把圖片粘貼到任何位置。

到此這篇關(guān)于WPF用狀態(tài)模式開發(fā)截圖功能的文章就介紹到這了,更多相關(guān)WPF截圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#使用GDI繪制矩形的方法

    C#使用GDI繪制矩形的方法

    這篇文章主要介紹了C#使用GDI繪制矩形的方法,涉及C#使用GDI繪圖的相關(guān)技巧,需要的朋友可以參考下
    2015-04-04
  • .Net?Core以windows服務方式部署

    .Net?Core以windows服務方式部署

    這篇文章介紹了.Net?Core以windows服務方式部署,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-01-01
  • C#排序算法之快速排序解析

    C#排序算法之快速排序解析

    這篇文章主要為大家詳細介紹了C#排序算法之快速排序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • C# 中this關(guān)鍵字的主要作用

    C# 中this關(guān)鍵字的主要作用

    this 關(guān)鍵字在C#中主要用于引用當前對象,區(qū)分字段與局部變量,調(diào)用其他構(gòu)造函數(shù)以及傳遞當前對象給其他方法或構(gòu)造函數(shù),本文重點介紹C# this關(guān)鍵字的作用,感興趣的朋友一起看看吧
    2024-02-02
  • C#異步編程由淺入深(三)之詳解Awaiter

    C#異步編程由淺入深(三)之詳解Awaiter

    這篇文章主要介紹了C#異步編程由淺入深(三)之詳解Awaiter,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • C#?WPF調(diào)用QT窗口的方法

    C#?WPF調(diào)用QT窗口的方法

    本文主要介紹了C#?WPF調(diào)用QT窗口的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • c# 在windows中操作IIS設置FTP服務器的示例

    c# 在windows中操作IIS設置FTP服務器的示例

    這篇文章主要介紹了c# 在windows中操作IIS設置FTP服務器的示例,幫助大家更好的理解和學習使用c#,感興趣的朋友可以了解下
    2021-03-03
  • C#中將xml文件反序列化為實例時采用基類還是派生類的知識點討論

    C#中將xml文件反序列化為實例時采用基類還是派生類的知識點討論

    在本篇文章里小編給大家整理的是關(guān)于C#中將xml文件反序列化為實例時采用基類還是派生類的知識點討論,有需要的朋友們學習下。
    2019-11-11
  • c#初學簡單程序?qū)嵗a介紹

    c#初學簡單程序?qū)嵗a介紹

    這篇文章介紹了c#初學簡單程序?qū)嵗a,有需要的朋友可以參考一下
    2013-10-10
  • C#實現(xiàn)標題閃爍效果的示例代碼

    C#實現(xiàn)標題閃爍效果的示例代碼

    在Windows系統(tǒng)中,當程序在后臺運行時,如果某個窗體的提示信息需要用戶瀏覽,該窗體就會不停地閃爍,這樣就會吸引用戶的注意,下面我們就來看看如何使用C#實現(xiàn)這一效果吧
    2024-04-04

最新評論