English 中文(简体)
从线索中的类访问动态控制
原标题:Access dynamic control from class in a thread

我正致力于一个小型项目,处理投票设备,以检查I/O控制状态。我实施了一个小型项目,处理特定设备,但决定我最终要实施不同的设备,因此转到一个类别:接口方法。但这引起了一些问题,因为我移动了很多代码。

之前,我移动代码 周围和这样, 我正在访问动态 表格控制 通过使用一个代表 这样的代表;

 if (result != null)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    txtOutput1.Text = (result[4] == 0x00 ? "HIGH" : "LOW"); // runs on UI thread

                    if (result[4] == 0x00)
                    {
                        this.Controls["btn" + buttonNumber].BackColor = Color.Green;
                    }
                    else
                    {
                        this.Controls["btn" + buttonNumber].BackColor = Color.Red;
                    }


                });

            }

这工作得很好, 直到我将某些方法移到从接口继承的新类别。 我不想仅仅将动态按钮设置为公共按钮, 并且我不确定我可以创建获取; 设置; 对于动态按钮, 考虑到其中有许多, 并且是在启动时创建。 另一个问题是这个. intoke 命令。 我相信 引用命令不起作用, 除非它被放置在窗体上... 而现在它被移动到一个类别, 所以我需要查看另一种方法来这样做 。

有谁知道我该往哪里走吗?

内容1:

此程序设计为用于处理输入/输出的硬件设备的监控系统。 使用这些i 可以检查是否触发了门警铃, 以及是否触发了门警。 程序本身在窗体/ 设计上非常简单 。 目前我有一个单一的窗体, 根据数据库中的信息生成按钮, 例如如果配置了 10 个设备, 则有 10 个按钮 。 每个按钮都显示硬件状态的绿色/ 红色依赖性 。

我的主窗体为每个监测它的设备触发线条,但因为我希望有多种类型的设备,我把它们移动到不同的类别和一个可以处理所有常见方法的界面。目前我有一个设备类别,它可以执行一个接口。关于这个问题,我需要现在访问一个我正在更新的单一主要形式的例子,而不是创建一个新的实例,这样我就可以使用当我把逻辑移到形式本身时所创建的新方法。

内容2:

    IdeviceInterface bfdeviceimp = new bf2300deviceimp();
   // some other declarations and initialize components

 private void btnConnect_Click(object sender, EventArgs e)
    {
        updateUI();
    }

    public void updateUI()
    {

        DBConnector mDBConnector = new DBConnector();
        int count = mDBConnector.Count() - 1;
        DataTable dataTable = mDBConnector.Select("SELECT * FROM devices");


        int x = 12;
        int y = 65;
        for (int i = 0; i <= count && i < 25; i++)
        {

            Button btnAdd = new Button();
            btnAdd.Text = dataTable.Rows[i]["deviceDescription"].ToString();
            btnAdd.Location = new Point(x, y);
            btnAdd.Tag = i;
            btnAdd.Name = "btn" + i.ToString();
            btnAdd.BackColor = Color.Green;
            var temp = i + 1;
            this.Controls.Add(btnAdd);

            this.Controls[btnAdd.Name].MouseClick += (sender, e) =>
            {
                int index = temp;
                generalMethods.generatePopup(sender, e, index);
            };

            string address = dataTable.Rows[i]["deviceIP"].ToString();
            int port = int.Parse(dataTable.Rows[i]["devicePort"].ToString());


            ThreadStart workerThread = delegate { start(address, port, i); };
            new Thread(workerThread).Start();

            x = (x + 75);
            if (i != 0 && (i % 5) == 0)
            {
                x = 12;
                y = y + 30;
            }
            if (i == 25)
            {
                Button btnPreviousPage = new Button();
                btnPreviousPage.Text = "<";
                btnPreviousPage.Location = new Point(150, 350);
                btnPreviousPage.Tag = "left";
                this.Controls.Add(btnPreviousPage);

                Button btnNextPage = new Button();
                btnNextPage.Text = ">";
                btnNextPage.Location = new Point(225, 350);
                btnNextPage.Tag = "right";
                this.Controls.Add(btnNextPage);
            }
        }
    }
    public void start(string address, int port, int i)
    {
        if (timer == null)
        {
            timer = new System.Timers.Timer(1000);


            timer.Elapsed += delegate(object sender, ElapsedEventArgs e) { timerElapsed(sender, e, address, port, i); };
        }
        timer.Enabled = true;
        // MessageBox.Show("Thread " + i + " Started.");
    }
    public void timerElapsed(object sender, ElapsedEventArgs e, string address, int port, int i)
    {
        bfdeviceimp.newconnect(address, port, i);
    }

最后,我的设备类:

   class bf2300deviceimp : IdeviceInterface
{
   public void newconnect(string address, int port, int buttonNumber)
    {
        //send data
        byte[] bData = new byte[71];
        bData[0] = 240;
        bData[1] = 240;
        bData[2] = 0;
        bData[3] = 1;
        bData[68] = 240;
        bData[69] = 240;
        bData[70] = this.newCalculateCheckSum(bData);


        try
        {
            byte[] result = this.newSendCommandResult(address, port, bData, 72);

           //form1.setAlarmColour(result, buttonNumber);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

    }

你建议我把换掉的接线员放在哪里?

最佳回答

您应该使用以事件为基础的方法来解决这个问题, 通常情况下, 在表格之间传递信息时, 您的每个设备应该有一个自定义的事件, 由它们来定义该设备状态改变时的发射。 事件应该仅仅在界面中定义, 以便与该设备进行互动。 窗体, 当它创建各种设备类别时, 应该订阅该事件, 而在事件处理器中, 它应该适当更新按钮/ 文本框 。

如果您不习惯这种编程风格的话,这可能会是相当不错的。 请随意在评论中要求更多细节,我可以解释为什么我这样做,或者它实际上做了什么。

public Form1()
{
    InitializeComponent();

    //not sure if this is on initialization or in a button click event handler or wherever.
    IDevice device = new SomeDevice();
    device.StatusChanged += GetHandlerForDevice(1);
    device.DoStuff();

    IDevice device2 = new SomeDevice(); //could be another class that implements IDevice
    device.StatusChanged += GetHandlerForDevice(2);
    device.DoStuff();
}

/// <summary>
/// The handlers for device status changed only vary based on the button number for each one.
/// This method takes a button number and returns an event handler that uses that button number.
/// </summary>
/// <param name="buttonNumber"></param>
/// <returns></returns>
private EventHandler<StatusChangedEventArgs> GetHandlerForDevice(int buttonNumber)
{
    //use currying so that the event handler which doesn t have an appropriate signature
    //can be attached to the status changed event.
    return (sender, args) => device_StatusChanged(sender, args, buttonNumber);
}

private void device_StatusChanged(object sender, StatusChangedEventArgs args, int buttonNumber)
{
    this.Invoke((MethodInvoker)delegate
    {
        txtOutput1.Text = (args.CurrentStatus == IDevice.Status.Green ? "HIGH" : "LOW"); // runs on UI thread

        if (args.CurrentStatus == IDevice.Status.Green)
        {
            this.Controls["btn" + buttonNumber].BackColor = Color.Green;
        }
        else
        {
            this.Controls["btn" + buttonNumber].BackColor = Color.Red;
        }


    });
}



public interface IDevice
{
    event EventHandler<StatusChangedEventArgs> StatusChanged;
    Status CurrentStatus { get; }


    public enum Status
    {
        Green,
        Red
    }

    void DoStuff();
    // rest of interface ...
}

public class StatusChangedEventArgs : EventArgs
{
    public IDevice.Status CurrentStatus { get; set; }
    //can add additional info to pass from an IDevice to a form if needed.
}

public class SomeDevice : IDevice
{
    public event EventHandler<StatusChangedEventArgs> StatusChanged;

    private IDevice.Status _currentStatus;
    /// <summary>
    /// Gets the current status of the device this object represents.
    /// When set (privately) it fires the StatusChanged event.
    /// </summary>
    public IDevice.Status CurrentStatus
    {
        get { return _currentStatus; }
        private set
        {
            _currentStatus = value;
            if (StatusChanged != null)
            {
                StatusChangedEventArgs args = new StatusChangedEventArgs();
                args.CurrentStatus = value;
                StatusChanged(this, args);
            }
        }
    }


    public void DoStuff()
    {
        //... do stuff
        CurrentStatus = IDevice.Status.Green; //will fire status changed event
    }
}
问题回答

将所有逻辑移入窗体中的方法中,然后将其外部使用。

以您的形式创建财产

public SynchronizationContext SyncContext { get; set;}

在构造器中添加:

this.SyncContext = WindowsFormsSynchronizationContext.Current;

Make 接口 :

public Interface IChangeClient 
{
    void Process(<some_type> result); // place your logic here
}

在表格中执行,以更改按钮和文字。

扩展您的原始界面以使用 SynchronizationContext Ibackground ChangeClient (可能作为一个参数)

比你的代码看起来是这样的:

if (result != null)
{
    oSyncContext.Post(new System.Threading.SendOrPostCallback(
            delegate(object state)
            {
                IBackgroundChangeClient client = (state as object[])[0] as IBackgroundChangeClient
                //i dont konw the type of this
                var innerResult= (state as object[])[1];
                client.Process(innerResult);
            }), new object[] { oBackgroundChangeClient, result[4]});

}




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...