简体   繁体   English

C# 如何“一般地”将 UI 控件引用传递给 class 模块并访问它的属性?

[英]C# How can I pass a UI control reference to a class module "generically" and access it' s properties?

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.我已经完成了大量的 C# 编程,但都是非常小的东西,并且来自 C 嵌入式领域的背景(不是 C++)我还没有完全接受 OO 方法,我很高兴地说我正在努力改变这一点。

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.我正在将我反复使用的一些串行通信代码重写到 Class 模块中,我可以将其放入未来的项目中并进行实例化。 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.除了一件事,我一切正常:日志记录 function,我将 com 个字符写入文本框,并将环形缓冲区的索引写入标签。 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).实际上,我可以使所有这些都起作用,但我希望更多地“概括”并传递具有环形缓冲区索引的“.Text”属性的任意数量的事物之一(例如,Label、TextBox 或工具条状态标签)。

Here's the example, say I have a form with a text box, a label, and a ToolStripStatusLabel.这是示例,假设我有一个带有文本框、label 和 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?) GUI 在一个线程上,而我的 class 模块在另一个线程上运行(主要是因为它正在处理串口,这对问题来说可能无关紧要?)

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.无论如何,假设我的 class 中有一个模块化变量(声明为“对象”?),我想在 object 中创建一个方法来传递对这三个 UI 元素中任何一个的引用,每个元素都有我可以写入的“.Text”属性。

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. Class 模块有一个要调用的委托,允许它写入名为 txtLog 的表单上的另一个 gui 元素,该元素以可视方式记录数据。 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).同时,我想向另一个传入的 UI object 写入一些内容(假设我想显示环形缓冲区中的索引变量)。

It works fine if I stick to a Label (or any one of them) and declare everything as a Label:如果我坚持使用 Label(或其中任何一个)并将所有内容声明为 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:然后是将赋值传递给 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:因为 class 和表单在不同的线程上,所以你需要使用 Invoke 东西:

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: “接收”为接收到字符时触发的事件运行,看起来像这样(“esLink”是我在此类中命名我的串行端口的名称),您可以看到调用“WriteData”发生将字符写入textbox 的 .Text 属性,它也“授予权利”(我知道这是错误的说法)在其下方的“WriteData”function 中的同一 UI 线程上将文本写入 label:

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.只要我将 class 中的变量声明为与我传入的类型相同的类型即可。但是我想将(几乎)任何具有 a.Text 属性的内容传递给它,这样我在设计 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.我尝试将传递的项目声明为 Object,它到达那里但 IDE 抱怨 object 没有 a.Text 属性。 I tried declaring it as something with a.Text property and then “changing” it with a “new” but that didn't work either.我尝试将它声明为具有 a.Text 属性的东西,然后用“新”属性“更改”它,但这也不起作用。 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).那里的问题是,如果我在顶部声明了三种不同的类型并且只使用了一种(并设置某种控制变量来决定在写入 UI 控件时使用哪一种),我只能使它起作用。

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.原则上,我想声明一个通用的 object,我可以根据传入的内容和访问其 .Text 属性将其转换为任何内容。 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.至少,为每种类型创建一个重载方法(实际上可能只有 4 或 5 种不同的类型)是可以接受的(但并不理想),我可以接受。

(I hope I have explained this well, sorry if not...) (我希望我已经解释清楚了,如果没有,请见谅……)

Thanks,谢谢,

-Vin -Vin

Honestly it's a little weird for a serial port library to have a dependency on a UI control (see separation of concerns ).老实说,串行端口库依赖于 UI 控件有点奇怪(请参阅关注点分离)。 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.他们可以使用具有Text属性的控件,或者如果他们愿意,可以使用不同类型的控件。 Or maybe they might not want to use a textbox, or a winforms control at all, but maybe log it to a file.或者他们可能根本不想使用文本框或 winforms 控件,但可能会将其记录到文件中。

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 .您还可以考虑摆脱不寻常的机制,转而采用更惯用的机制,例如自定义事件

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.您可以将(参数的?)类型定义为“Control”(System.Windows.Forms.Control),因为大多数UI控件类都是从这个class派生的。实际上,Control class确实具有大量属性,例如“文本”、“位置”、“大小”、“父级”等。

See https://learn.microsoft.com/do.net/api/system.windows.forms.control参见https://learn.microsoft.com/do.net/api/system.windows.forms.control

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 来自C#WPF中另一个类的访问控制属性 - Access control's properties from another class in C# WPF C# - 在通用 Class 中,如何设置通用类型的 Function 委托? - C# - In a Generic Class, How Can I Set a Generically-Typed Function Delegate? C#泛型:我如何一般地使用它们? - C# Generics: How can I use them generically? 如何在C#中通用实例化通用类? - How to generically instantiate a generic class in C#? 在C#中为什么我不能传递另一个类'EventHandler引用,我该如何绕过它? - In C# why can't I pass another class' EventHandler reference and how can I get around it? 如何在C#中通过引用传递属性? - How to pass properties by reference in c#? 如何在 C# 中访问泛型基类参数的属性? - How to access properties of generic base class's parameter in C#? C# - 使用对基类的引用,如果我知道我的引用是对继承类的引用,我如何访问继承类的属性? - C# - with a reference to a base class, how can I access a property of the inherited class if I know that my reference is to the inherited class? C# WinUI3 桌面:如何引用或控制 ListView 的所选项目 DataTemplate 中的按钮? - C# WinUI3 Desktop: How can I reference or control a button in a ListView's selected item DataTemplate? 如何在C#中从IDataReader Func调用的另一个属性中访问类属性 - How can i access a class properties from within another one called from a IDataReader Func in C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM