【问题标题】:Dynamic Classes Handling Events and Label Creation Text Adventure C# WPF动态类处理事件和标签创建文本冒险 C# WPF
【发布时间】:2018-08-03 01:00:44
【问题描述】:

我目前正在做一个基于房间的文字游戏。我在 C# 方面相当缺乏经验,因此任何解决方案很可能需要一些示例或一些资源来提供帮助。 话虽如此,我的问题是每次我创建一个新的“房间”时,我都需要重做很多工作,并且我想知道如何制作一个 Room 类来做我想做的事情。

所以基本上,每次我创建一个房间时,我都会这样做: 1.初始化一些包含我的文本的运行。 2.初始化一些代表我的导航事件的可点击标签。 3.初始化与标签匹配的事件。

所有这些都相当耗时,每次我都必须重做,而且很容易出错。

它是使用 FlowDocument 在 C# WPF 中完成的,因为它是一个文本冒险游戏

这是我的代码:

{
    public MainWindow()
    {
        InitializeComponent();
        StartingArea();
        myFlowDocument.Blocks.Add(myParagraph);
    }
    Paragraph myParagraph = new Paragraph();
    Paragraph myParagraph2 = new Paragraph();
    public void StartingArea()
    {

        InlineLabel lStartingAreaLook = new InlineLabel("look.");
        lStartingAreaLook.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaLook_Click);
        ModInlineUIContainer lStartingArea_Look = new ModInlineUIContainer(lStartingAreaLook);

        object[] pStartingRoom = { tStartingText, lStartingArea_Look };
        AddInline(pStartingRoom);
        this.Content = myFlowDocument;
        void ClearParagraph()
        {
            foreach (Inline run in myParagraph.Inlines.ToList())
            {
                myParagraph.Inlines.Remove(run);
            }
        }
        void lStartingAreaLook_Click(object sender, MouseButtonEventArgs e)
        {
            ClearParagraph();
            StartingAreaLook();
        }
        void StartingAreaLook()
        {

            InlineLabel lStartingAreaLook_Speak = new InlineLabel("speak");
            lStartingAreaLook_Speak.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaLook_Speak_Click);
            ModInlineUIContainer cStartingAreaSpeak = new ModInlineUIContainer(lStartingAreaLook_Speak);

            InlineLabel lStartingArea_Use = new InlineLabel("use");
            lStartingArea_Use.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaLook_Use_Click);
            ModInlineUIContainer cStartingAreaUse = new ModInlineUIContainer(lStartingArea_Use);

            object[] pStartingArea2 = { tStartingAreaLook, cStartingAreaSpeak, tStartingAreaLook2, cStartingAreaUse, tStartingAreaLook3 };
            AddInline(pStartingArea2);
            void lStartingAreaLook_Speak_Click(object sender, MouseButtonEventArgs e)
            {
                ClearParagraph();
                StartingAreaSpeak();
                //myParagraph.Inlines.Add(tStartingAreaLook);
            }
            void lStartingAreaLook_Use_Click(object sender, MouseButtonEventArgs e)
            {
                ClearParagraph();
                StartingAreaUse();
                //myParagraph.Inlines.Add(tStartingAreaLook);
            }
            void StartingAreaSpeak()
            {

                InlineLabel lStartingAreaSpeak_Look = new InlineLabel("look");
                lStartingAreaSpeak_Look.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaSpeak_Look_Click);
                ModInlineUIContainer cStartingAreaSpeak_Look = new ModInlineUIContainer(lStartingAreaSpeak_Look);

                InlineLabel lStartingAreaSpeak_Use = new InlineLabel("use");
                lStartingAreaSpeak_Use.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaSpeak_Use_Click);
                ModInlineUIContainer cStartingAreaSpeak_Use = new ModInlineUIContainer(lStartingAreaSpeak_Use);

                object[] pStartingAreaSpeak = { tStartingAreaSpeak, cStartingAreaSpeak_Look, tStartingAreaSpeak2, cStartingAreaSpeak_Use, tStartingAreaSpeak3 };
                AddInline(pStartingAreaSpeak);
                void lStartingAreaSpeak_Look_Click(object sender, MouseButtonEventArgs e)
                {
                    ClearParagraph();
                    StartingAreaLook();
                    //myParagraph.Inlines.Add(tStartingAreaLook);
                }
                void lStartingAreaSpeak_Use_Click(object sender, MouseButtonEventArgs e)
                {
                    ClearParagraph();
                    StartingAreaUse();
                    //myParagraph.Inlines.Add(tStartingAreaLook);
                }
            }
            void StartingAreaUse()
            {

                Run tStartingArea_Use = new Run($@"{sUse}");

                InlineLabel lStartingArea_Use_Restart = new InlineLabel("Restart");
                lStartingArea_Use_Restart.MouseDoubleClick += new MouseButtonEventHandler(lStartingArea_Use_Restart_Click);
                ModInlineUIContainer cStartingArea_Use_Restart = new ModInlineUIContainer(lStartingArea_Use_Restart);

                object[] pStartingAreaUse = { tStartingArea_Use, cStartingArea_Use_Restart };

                AddInline(pStartingAreaUse);


                void lStartingArea_Use_Restart_Click(object sender, MouseButtonEventArgs e)
                {
                    ClearParagraph();
                    StartingArea();
                    //myParagraph.Inlines.Add(tStartingAreaLook);
                }
            }
        }
    }
    private void RemoveDoubleClickEvent(Label b)
    {
        FieldInfo f1 = typeof(Control).GetField("EventDoubleClick",
            BindingFlags.Static | BindingFlags.NonPublic);
        object obj = f1.GetValue(b);
        PropertyInfo pi = b.GetType().GetProperty("Events",
            BindingFlags.NonPublic | BindingFlags.Instance);
        EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
        list.RemoveHandler(obj, list[obj]);
    }
    public void AddInline(object[] inline)
    {
        foreach(dynamic element in inline)
        {
            myParagraph.Inlines.Add(element);
        }
    }
    public void RemoveInline(object[] inline)
    {
        foreach (dynamic element in inline)
        {
            myParagraph.Inlines.Add(element);
        }
    }
}

现在我想做的就是创建一个类,我可以在每次制作新房间时使用它。问题是我需要为每个事件创建事件和未知数量的标签。

我基本上希望能够做类似的事情:

{
Public void Livingroom()
{

    Room Livingroom = new Room();
    SetupRoom(Room.Livingroom, 3, 2);
    //Whereas the first is the room object, the second is the amount of Run, the third is the amount of labels
    object[] LivingroomParagraph = { Livingroom.Run1, Livingroom.Run2, Livingroom.Label1, Livingroom.Run3, Livingroom.Label2 };
    AddInline(LivingroomParagraph);
    void lLivingroom1(object sender, MouseButtonEventArgs e)
    {
        //Do something
    }

    void lLivingroom2(object sender, MouseButtonEventArgs e)
    {
        //Do something

    }

}

}

所以基本上我需要一个能够动态生成的构造函数,尤其是那些部分:

Run tStartingArea_Use = new Run($@"{sUse}");

                InlineLabel lStartingArea_Use_Restart = new InlineLabel("Restart");
                lStartingArea_Use_Restart.MouseDoubleClick += new MouseButtonEventHandler(lStartingArea_Use_Restart_Click);
                ModInlineUIContainer cStartingArea_Use_Restart = new ModInlineUIContainer(lStartingArea_Use_Restart);

这个比较复杂,希望能得到帮助!

【问题讨论】:

  • 您可能会发现使用TreeLinked List 很有用。这些数据结构是由称为“节点”的元素组成的列表。每个节点都包含对其他节点的引用(或链接)以及数据(int/string/class/etc.)。这些数据结构具有顺序访问而不是索引访问的优点。即。 var next = currNode.next()var next = arr[++index]。还有一种叫做Graph的数据结构思想,它没有任何关于它有哪些子/父节点的规则。
  • Graph 数据结构更像树,而不是 x、y 或 z 图的数学概念。
  • @BennettYeo - 更重要的是,树是图的特例。

标签: c# wpf class flowdocument adventure


【解决方案1】:

我认为您不必费心创建一个类来使这对您更容易。你实际上只是在重复很多代码,你可以很好地把它们包装成一个函数。

您要重复的关键是创建ModInlineUIContainer 对象。我建议您制定一种方法来为您完成艰苦的工作:

private ModInlineUIContainer CreateContainer(string text, Action mouseDoubleClick)
{
    var label = new InlineLabel(text);
    var container = new ModInlineUIContainer(label);

    label.MouseDoubleClick += (s, e) => mouseDoubleClick();

    return container;
}

.MouseDoubleClick += (s, e) => 语法减少了创建单独方法的需要,Action mouseDoubleClick 的使用让您可以在引发 MouseDoubleClick 时传递您想要执行的任何操作。

我所做的另一个重构是从每个事件处理程序中删除 ClearParagraph(); 方法调用,并将其放入每个 StartingArea*() 方法的开头。

也就是说我可以替换这段代码:

InlineLabel lStartingAreaLook_Speak = new InlineLabel("speak");
lStartingAreaLook_Speak.MouseDoubleClick += new MouseButtonEventHandler(lStartingAreaLook_Speak_Click);
ModInlineUIContainer cStartingAreaSpeak = new ModInlineUIContainer(lStartingAreaLook_Speak);

void lStartingAreaLook_Speak_Click(object sender, MouseButtonEventArgs e)
{
    ClearParagraph();
    StartingAreaSpeak();
}

...用这个:

    ModInlineUIContainer speak = CreateContainer("speak", () => StartingAreaSpeak());

然后我将AddInLine 方法的参数更改为具有签名params object[] inline,这样我就可以直接调用它而无需先创建数组。

现在剩下的代码很好地退出了。它看起来像这样:

    public MainWindow()
    {
        InitializeComponent();
        StartingArea();
        myFlowDocument.Blocks.Add(myParagraph);
        this.Content = myFlowDocument;
    }

    Paragraph myParagraph = new Paragraph();

    private ModInlineUIContainer CreateContainer(string text, Action mouseDoubleClick)
    {
        var label = new InlineLabel(text);
        var container = new ModInlineUIContainer(label);

        label.MouseDoubleClick += (s, e) => mouseDoubleClick();

        return container;
    }

    public void StartingArea()
    {
        ClearParagraph();
        var look = CreateContainer("look", () => StartingAreaLook());
        AddInline(tStartingText, look);
    }

    void StartingAreaLook()
    {
        ClearParagraph();
        var speak = CreateContainer("speak", () => StartingAreaSpeak());
        var use = CreateContainer("use", () => StartingAreaUse());
        AddInline(tStartingAreaLook, speak, tStartingAreaLook2, use, tStartingAreaLook3);
    }

    void StartingAreaSpeak()
    {
        ClearParagraph();
        var look = CreateContainer("look", () => StartingAreaLook());
        var use = CreateContainer("use", () => StartingAreaUse());
        AddInline(tStartingAreaSpeak, look, tStartingAreaSpeak2, use, tStartingAreaSpeak3);
    }

    void StartingAreaUse()
    {
        ClearParagraph();
        var tStartingArea_Use = new Run($"{sUse}");
        var restart = CreateContainer("Restart", () => StartingArea());
        AddInline(tStartingArea_Use, restart);
    }

    void ClearParagraph()
    {
        foreach (Inline run in myParagraph.Inlines.ToList())
        {
            myParagraph.Inlines.Remove(run);
        }
    }

    public void AddInline(params object[] inline)
    {
        foreach (dynamic element in inline)
        {
            myParagraph.Inlines.Add(element);
        }
    }

这样代码重复应该会少很多。


如果你想得到所有时髦的猴子,那么你可以试试这个:

public MainWindow()
{
    InitializeComponent();

    myFlowDocument.Blocks.Add(myParagraph);
    this.Content = myFlowDocument;

    _areas = new Dictionary<string, Func<object[]>>()
    {
        { "start", () => new object[] { CreateContainer("look") } },
        { "look", () => new object[] { tStartingAreaLook, CreateContainer("speak"), tStartingAreaLook2, CreateContainer("use"), tStartingAreaLook3 } },
        { "speak", () => new object[] { tStartingAreaLook, CreateContainer("look"), tStartingAreaLook2, CreateContainer("use"), tStartingAreaLook3 } },
        { "use", () => new object[] { new Run($"{sUse}"), CreateContainer("start") } },
    };

    Starting("start");
}

private void Starting(string key)
{
    foreach (Inline run in myParagraph.Inlines.ToList())
    {
        myParagraph.Inlines.Remove(run);
    }
    foreach (dynamic element in _areas["look"]())
    {
        myParagraph.Inlines.Add(element);
    }
}

Paragraph myParagraph = new Paragraph();

Dictionary<string, Func<object[]>> _areas;

private ModInlineUIContainer CreateContainer(string text)
{
    var label = new InlineLabel(text);
    var container = new ModInlineUIContainer(label);

    label.MouseDoubleClick += (s, e) => Starting(text);

    return container;
}

【讨论】:

  • 这太棒了!非常感谢!如果不是几天的摆弄,你为我节省了几个小时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-13
  • 2015-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
相关资源
最近更新 更多