分类 C# 下的文章

条码打印机打印

这两天在准备一下自己的简历,相信自己。

好久没有写C# 的程序了,最近需要用到条码打印机,因为在大学做项目的时候接触过打印机这一块,所以拿来做就可以了。这篇主要记录下思路吧

- 阅读剩余部分 -

跨线程调用invoke导致界面卡死

跨线程操作控件

首先相关的文章在其他人的博客上已经有不少了,这里也是参考了其他人的写法。
在实际的开发中,经常需要在另外的线程中操作界面的控件,这个时候因为创建控件的UI线程和实际的工作线程不是同一个线程,所以会报一个不允许的操作异常,这个时候有几种解决方法。

  1. 直接禁用编译器做检查,这个不推荐(都是这么说的,我也就没用过)

//在窗口初始化完成后加入这一行
Control.CheckForIllegalCrossThreadCalls = false;
  1. 使用Invoke,模板代码

//
    private delegate void SetButtonTextDelegate(Button b, string text);
    private void SetButtonText(Button b, string text)
    {
        if (this.InvokeRequired)
        {
            SetButtonTextDelegate setButtonTextDelegate = new SetButtonTextDelegate(SetButtonText);
            b.Invoke(setButtonTextDelegate, new object[] { b, text });
        }
        else
        {
            b.Text = text;
        }
    }

    //在调用的时候
    SetButtonText(btn**,"测试数据");

InvokeRequired方法代表的是操作控件的线程是不是创建控件的线程,不是的话为True就调用Invoke,是的话直接执行b.Text = text
可以这样还是有问题,我在触发事件的时候还是会卡住,所以使用了下面的方法

  1. 使用BeginInvoke

       private delegate void SetButtonTextDelegate(Button b, string text);
       private void SetButtonText(Button b, string text)
       {
           if (this.InvokeRequired)
           {
               SetButtonTextDelegate setButtonTextDelegate = new SetButtonTextDelegate(SetButtonText);
               b.BeginInvoke(setButtonTextDelegate, new object[] { b, text });
           }
           else
           {
               b.Text = text;
           }
       }
    

仅仅是把上面的Invoke换成了BeginInvoke解决了问题。

.NET-多线程如何使用

在回顾一下,前段时间使用过忘记了。

AutoResetEvent类

通知正在等待的线程已发生事件,此类不能被继承

主要使用的方法有两个:

  1. Set()

将事件设置为终止状态,允许一个或多个等待线程继续

  1. WaitOne()
    阻止当前线程,直至当前WaitHandle收到信号,其实就是收到Set信号

在构造函数中可以使用bool来决定一开始是终止的还是非终止的。

栗子

class HeiSheHhui
    {
        private static AutoResetEvent startSighal = new AutoResetEvent(false);

        public static void Test()
        {
            Thread xiaodiThread = new Thread(ThreadProc);
            xiaodiThread.Start();
            for (int i = 0; i < 2; i++)
            {
                Console.ReadLine();
                startSighal.Set();
            }
            Console.WriteLine("结束");
            Console.ReadKey();
        }
        
        static void ThreadProc()
        {
            Console.WriteLine("小弟们都已经准备好了,就等老大发话了。。。。按回车我们就上");
            startSighal.WaitOne();
            Console.WriteLine("兄弟们上啊");
            Console.WriteLine("小弟们又准备好了,就等老大发话了。。。。按回车我们就上,再打一次");
            startSighal.WaitOne();
            Console.WriteLine("兄弟们上啊");
        }
    }

举一个黑社会的例子,只有收到Set信号后,小弟们才会冲上去,否则会原地等待。
运行结果

这样就可以开控制线程里面的状态了。

CancellationTokenSource 类

向应该被取消的CancellationToken发送信号

主要用到的是:

  1. 构造函数可以指定延迟时间传达取消信号
    CancellationTokenSource()CancellationTokenSource(Int32)

  2. Cancel()
    传达取消请求

  3. IsCancellationRequested()
    看看是不是已经取消了

栗子

class Example
    {
        static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        public static void Test()
        {
            Thread thread = new Thread(new ThreadStart(() =>
            {
                while (!cancellationTokenSource.IsCancellationRequested)
                {
                    Console.WriteLine("还在执行。。。。");
                    Thread.Sleep(1000);
                }
                Console.WriteLine("好,执行完成了");
            }));
            thread.Start();

            Console.WriteLine("按回车键发送信号取消线程的执行。。。。");
            Console.ReadLine();
            cancellationTokenSource.Cancel();
            Console.WriteLine("取消了。。。");
            Console.ReadLine();
        }
    }

运行结果

System.Collections.Consurrent命名空间

System.Collections.Concurrent命名空间提供多个线程安全集合类,当有多个线程并发访问集合时,应使用这些类代替System.CollectionsSystem.Collections.Generic命名空间中的对应类型

经常使用的类

  1. BlockingCollection<T>
    为实现IProducerConsumerCollection<T>的线程安全集合提供阻塞和限制功能

  2. ConcurrentBag<T>
    线程安全的对象无序集合,包

  3. ConcurrentDictionary<TKey,TValue>ConcurrentQueue<T>ConcurrentStack<T>
    从名字中就能看出来是字典、队列和栈

ConcurrentQuent 栗子

class CQ_EnqueueDequeuePeek
{
    // Demonstrates:
    // ConcurrentQueue<T>.Enqueue()
    // ConcurrentQueue<T>.TryPeek()
    // ConcurrentQueue<T>.TryDequeue()
    public static void Test()
    {
        // Construct a ConcurrentQueue.
        ConcurrentQueue<int> cq = new ConcurrentQueue<int>();

        // Populate the queue.
        for (int i = 0; i < 10000; i++) cq.Enqueue(i);

        // Peek at the first element.
        int result;
        if (!cq.TryPeek(out result))
        {
            Console.WriteLine("CQ: TryPeek failed when it should have succeeded");
        }
        else if (result != 0)
        {
            Console.WriteLine("CQ: Expected TryPeek result of 0, got {0}", result);
        }

        int outerSum = 0;
        // An action to consume the ConcurrentQueue.
        Action action = () =>
        {
            int localSum = 0;
            int localValue;
            while (cq.TryDequeue(out localValue)) localSum += localValue;
            //为多个线程共享的变量提供原子操作
            Interlocked.Add(ref outerSum, localSum);
        };

        // Start 4 concurrent consuming actions.
        Parallel.Invoke(action, action, action, action);

        Console.WriteLine("outerSum = {0}, should be 49995000", outerSum);
    }
}

Action的用法和几个多线程时需要用的操作。

小知识点集锦

一、 浮点数和0的比较
今天在做五参数几个数值处理的时候,因为为0的值要舍去,刚开始写了一行代码


if(x != 0){
//xxx
}

后来一想,不大对啊,感觉不对。上网一搜,上面的是大忌
正确的做法

if(x <= 0.000001 && x >= -0.000001)

原因:

float存在精度问题。整数部分越大,小数部分的精度就越低。0.100.1是两个不同的数。

二、 ListBox绑定到List集合或ObservableCollection,(问题仍然没有解决)
仅仅是数据绑定List上的话非常简单

public List<int> PhList = new List<int>(){1,2,3,4,5,6};
    public ObservableCollection<int> PhCollection = new ObservableCollection<int>(){1,2,3,4,5,6};

    public int Flag = 0;

    public MainWindow()
    {
        InitializeComponent();
        LbPhList.ItemsSource = PhList;
//      LbPhList.ItemsSource = PhCollection;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Flag ++;
        PhList.Add(Flag);
//      PhCollection.Add(Flag);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(Flag.ToString());
    }

现在的问题是:当我往集合中添加数据的话,ListBox并不会自动更新,这里就引出来了ObserableCollection,取消上上述代码的注释,可以看到ListBox可以更新数据了。
为什么:

这就是为什么ObservableCollection<T>会存在。ObservableCollection<T>实现了INotifyCollectionChanged,允许去通知UI集合中的数据被添加、删除或者替换了。List<T>没有触发任何通知,所以UI不能检测里面的内容发生了变化。来自stackoverflow

问题又来了:在Code Behind文件下没有任何问题,可以用上面代码的方式通知UI进行更新,在MVVM模式下,不起作用了。使用ObservableCollection也不行。(因为这个东西已经快疯掉了)
出现异常成为我的最爱:

对此问题无效现在我想把我的操作系统换成英文的了

三、 函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。

改 [DllImport("xxxx.dll", EntryPoint = "xx",)]
为[DllImport("xxxx.dll", EntryPoint = "xx", CallingConvention = CallingConvention.Cdecl)]
四、WPF中双击事件
MouseDown的事件里直接判断

    if(e.ClickCount ==2){
        //双击事件
    }

五、C# Float和double区别
float关键字表示存储32位浮点值得简单类型。默认情况下,赋值运算符右侧的实数被视为double。因此,应该使用fF出事啊花浮点型变量

float x = 3.5f;

如果不使用后缀,则会因为尝试将一个double值存储在float变量中而发生编译错误。
所有的浮点型变量都存在精度损失的问题。double是双精度,double保存32位,float保存16位。
六、MVVMLight 传递参数
How to uas RelayCommand with the MVVM Light framework,最重要的就是PassEnevtArgsToCommand = "True"
七、键盘控制


未完待续