c#如何将UI控件引用引用到类模块“一般”并访问它' s属性?

发布于 2025-01-23 16:52:19 字数 2583 浏览 0 评论 0原文

我做了很多C#编程,但这都是很小的东西,来自嵌入式空间中的C背景(不是C ++),我还没有完全接受OO方法,我很高兴地说我试图改变这一点。

我正在重写一些我一遍又一遍地使用的串行通信代码,这些代码可以放入以后的项目并实例化。除了一件事之外,我还有所有工作:记录功能,将COM字符写入文本框,以及从环形缓冲区到标签的索引。实际上,我可以使这一切都起作用,但是我希望更多地“概括”并通过“ .TEXT”属性的任何数量来传递环形缓冲区索引(例如,标签,文本框或一个woolstripstatuslabel)。

这是一个例子,例如我有一个带有文本框,标签和工具标签的表单。 GUI在一个线程上,我的类模块在另一个线程上运行(主要是因为它正在处理串行端口,这可能与问题无关紧要吗?

) “对象”?),我想在对象中创建一个方法,以将其重新传递给这三个UI元素中的任何一个,每个元素都具有我可以写入的“ .text”属性。

类模块有一个要调用的代表,它将允许其写入名为TXTLOG的表单上的另一个GUI元素,该表单在视觉上记录数据。同时,我想为其他传递的UI对象写一些东西(假设我想从环形缓冲区显示索引变量)。

如果我坚持使用标签(或其中任何一个)并将所有内容声明为标签

=============================

: 保存控制参考:

system.windows.forms.label inptrlbl;

,然后是将分配传递到类的方法:

public void TurnOnLogging(System.Windows.Forms.TextBox location, System.Windows.Forms.Label inLbl, System.Windows.Forms.Label outLbl)
{
    comLogging = true;
    logBox = location;
    inPtrLbl= new System.Windows.Forms.Label();
    inPtrLbl = inLbl;
}

因为类和表单在不同的线程上,所以您需要使用调用内容:

private delegate void UpdateUiTextDelegate(byte text, Int32 ptr);

“接收”是在收到炭时开火的事件的“接收”(“ Eslink”是我在此类中名称的串行端口),您可以看到“ Writedata”的调用恰好是写作char进入文本框的.TEXT属性,该属性也“授予正确的正确”(我知道这是“错误的话),将文本写入其下面的“写入”功能的同一UI线程上的标签

private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    byte recieved_data;

    // Collecting the characters received to our 'buffer' (string).
    while (esLink.BytesToRead > 0)
    {
        recieved_data = (byte)esLink.ReadByte();

        // add it to the circular buffer
        rxBuffer[nextBufferInputPoint] = recieved_data;
        if (++nextBufferInputPoint >= RX_BUFFER_SIZE)
            nextBufferInputPoint = 0;

        // then put it in the text box for logging
        logBox.Invoke(new UpdateUiTextDelegate(WriteData), recieved_data, nextBufferInputPoint);
    }
}

private void WriteData(byte text, Int32 ptr)
{
    // Assign the value of the recieved_data to the TextBox and label.
    if (comLogging)
    {
        logBox.Text += (char)text;
        inPtrLbl.Text = ptr.ToString();
    }
}

:确实,这就像魅力一样。只要我将班级中的变量声明为与所传递的变量相同。但是我想通过(几乎)具有.text属性的任何内容,因此我在设计GUI方面具有更大的灵活性。我尝试将传递的项目声明为对象,它到达那里,但IDE抱怨该对象没有.TEXT属性。我尝试将其宣布为.text属性,然后用“新”“更改”它,但这也不起作用。我说,好的,我将其限制为三种类型,并为这三种类型创建过载的方法。问题是,我只有在顶部声明三种不同类型而只使用一个(并设置某种控制变量来决定在写入UI控件时要使用的一个不同类型)时,我才能做出这项工作。

我认为必须有一种更简单的方法。原则上,我想声明一个通用对象,可以根据我传递的内容并访问其.TEXT属性,该对象可以变成任何内容。至少,为每种类型创建一个超载方法(实际上可能有4或5种不同类型)是可以接受的(但不是理想的),我可以接受。

(我希望我对此很好解释,对不起,如果没有...)

谢谢

- vin

I have done a fair amount of C# programming, but it's been all very small stuff, and coming from a C background (not C++) in the embedded space I haven't fully embraced the OO approach, and I'm happy to say I'm trying to change that.

I'm rewriting some serial comm code I've used over and over into a Class Module that I can just drop into future projects and instantiate. I have everything working except one thing: a logging function where I write the com characters to a textbox and the indices from a ring buffer into labels. I can make that all work, actually, but I was hoping to "generalize" more and pass one of any number of things with a ".Text" property for the ring buffer indices (for example, a Label, a TextBox, or a ToolStripStatusLabel).

Here’s the example, say I have a form with a text box, a label, and a ToolStripStatusLabel. The GUI is on one thread and my class module is running on another one (mostly because it is dealing with the serial port, which is perhaps inconsequential to the question?)

Anyway, lets say I have a modular variable in my class (declared as “Object”?) and I want to create a method in the object to pass in a refence to any one of those three UI elements, each one of which has the “.Text” property to which I can write.

The Class module has a delegate to invoke that will allow it to write to another gui element on the form called txtLog which is visually logging the data. At the same time I want to write something to this other passed-in UI object (say I want to display the index variable from the ring buffer).

It works fine if I stick to a Label (or any one of them) and declare everything as a Label:

===================

Up at the top, the modular variable to hold the control reference:

System.Windows.Forms.Label inPtrLbl;

And then a method to pass the assignment into the class:

public void TurnOnLogging(System.Windows.Forms.TextBox location, System.Windows.Forms.Label inLbl, System.Windows.Forms.Label outLbl)
{
    comLogging = true;
    logBox = location;
    inPtrLbl= new System.Windows.Forms.Label();
    inPtrLbl = inLbl;
}

Because the class and the form are on different threads, you need to use the Invoke stuff:

private delegate void UpdateUiTextDelegate(byte text, Int32 ptr);

“Receive” which runs for the event that fires when a char is received looks like this (“esLink” is what I named my serial port inside this class) and you can see the Invoke of “WriteData” happening to write the char into the textbox’s .Text property, which also “grants the right” (I know that’s’ the wrong thing to say) to write the text into the label on the same UI thread in the “WriteData” function below it:

private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    byte recieved_data;

    // Collecting the characters received to our 'buffer' (string).
    while (esLink.BytesToRead > 0)
    {
        recieved_data = (byte)esLink.ReadByte();

        // add it to the circular buffer
        rxBuffer[nextBufferInputPoint] = recieved_data;
        if (++nextBufferInputPoint >= RX_BUFFER_SIZE)
            nextBufferInputPoint = 0;

        // then put it in the text box for logging
        logBox.Invoke(new UpdateUiTextDelegate(WriteData), recieved_data, nextBufferInputPoint);
    }
}

private void WriteData(byte text, Int32 ptr)
{
    // Assign the value of the recieved_data to the TextBox and label.
    if (comLogging)
    {
        logBox.Text += (char)text;
        inPtrLbl.Text = ptr.ToString();
    }
}

So, all this works like a charm, really. As long as I declare the variable in the class to be the same type as what I’m passing in. But I want to pass (almost) anything with a .Text property to it so I have more flexibility in designing my GUI. I tried declaring the passed item as an Object, it gets there but the IDE complains that the object doesn’t have a .Text property. I tried declaring it as something with a .Text property and then “changing” it with a “new” but that didn’t work either. I said, ok, I’ll limit it to three types and create overloaded methods for the three types. The problem there is I could only make that work if I declared the three different types at the top and only used one (and set some kind of control variable to decide which one to use when writing to the UI control).

I’m thinking there has to be an easier way. In principle, I want to declare a generic object that I can turn into anything based on what I pass in and access its .Text property. At the very least, creating an overloaded method for each type (realistically there might be 4 or 5 different types only) would be acceptable (but not ideal) and I could live with that.

(I hope I have explained this well, sorry if not...)

Thanks,

-Vin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

铜锣湾横着走 2025-01-30 16:52:19

老实说,串行端口库对UI控件有依赖性有点奇怪(请参阅 noreflowl noreferrer“ /a>)。我建议您设置内容,以便呼叫者可以通过代表。

Action<char> _logHandler;

public void TurnOnLogging(Action<char> logHandler)
{
    comLogging = true;
    _logHandler = logHandler;
}

然后,当您有数据登录时,请致电委托。

private void WriteData(byte text)
{
    if (comLogging)
    {
        _logHandler((char)text);
    }
}

这样,呼叫者可以决定如何显示内容。他们可以使用具有文本属性的控件,或者如果愿意的话,可以使用其他类型的控件。或者,也许他们可能不想使用文本框或Winforms控件,但也许将其记录到文件中。

obj.TurnOnLogging( x => TextBox1.Text += x.ToString() );

或者

obj.TurnOnLogging( x => SomeOtherControl1.Caption += x.ToString() );

https://learn.microsoft.com/en-us/dotnet/stant/standnet/standard/standard/estard/events/howwents/how-how-to-to-to-

obj.TurnOnLogging( x => _logger.Write(x) );

您也可能会考虑摆脱异常机制,以支持更惯用的东西,例如a 自定义事件

Honestly it's a little weird for a serial port library to have a dependency on a UI control (see separation of concerns). I'd suggest you set things up so the caller can pass a delegate instead.

Action<char> _logHandler;

public void TurnOnLogging(Action<char> logHandler)
{
    comLogging = true;
    _logHandler = logHandler;
}

Then, when you have data to log, call the delegate.

private void WriteData(byte text)
{
    if (comLogging)
    {
        _logHandler((char)text);
    }
}

This way the caller can decide how the contents are displayed. They can use a control that has a Text property, or a different type of control if they want to. Or maybe they might not want to use a textbox, or a winforms control at all, but maybe log it to a file.

obj.TurnOnLogging( x => TextBox1.Text += x.ToString() );

Or

obj.TurnOnLogging( x => SomeOtherControl1.Caption += x.ToString() );

Or

obj.TurnOnLogging( x => _logger.Write(x) );

You might also consider getting rid of your unusual mechanism in favor of something more idiomatic, such as a custom event.

墟烟 2025-01-30 16:52:19

您可以将(参数的类型?)定义为“控制”(System.windows.forms.control),因为大多数UI控制类都是从此类派生的。实际上,控制类确实具有大量属性,例如“文本”,“位置”,“大小”,“ parent”等。

请参阅 https://learn.microsoft.com/dotnet/api/system.windows.windows.forms.control

You may define the type (of the parameter?) as "Control" (System.Windows.Forms.Control), as most UI control classes are derived from this class. Actually, the Control class has really a large number of properties, such as "Text", "Location", "Size", "Parent", etc.

See https://learn.microsoft.com/dotnet/api/system.windows.forms.control

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文