По новым правилам не нужно указывать ключевое слово void в параметрах для процедуры, если у неё отсутствуют параметры.
// CS1536.cs class a {publicstaticint x(void) // эта строка вызовет ошибку CS1536// Чтобы устранить ошибку, попробуйте определить функцию так:// public static int x(){return0;}publicstaticvoid Main(){} }
Такая ошибка возникала при двойном клике на файле солюшена *.sln. Если просто запустить Visual Studio 2010 ярлыком из меню "Пуск", то Visual Studio 2010 запускается без ошибки, и позволяет открыть тот же файл *.sln.
Нужно в свойствах ListBox поменять свойство Anchor (привязка к границам) на Top, Bottom, Left, Right. Для того, чтобы отделить некоторую часть формы от других элементов пользовательского интерфейса, применяйте элемент Panel, на котором размещайте другие элементы интерфейса.
В среде Visual Studio C# удобно делать пакет установки, не нужно разбираться с инсталляторами программы (внешние дополнительные инсталляторы теперь не нужны), которую Вы пишете. Для этого в меню достаточно выбрать в меню Построение (Build) -> Опубликовать (Release). Это так называемая технология ClickOnce [8]. В папке Release сразу появится подпапка app.publish, в которой будет лежать готовый инсталлятор setup.exe, содержащий все необходимые для установки программы компоненты.
Проблема обычно возникает, когда необходимо получить доступ на запись к одному и тому же ресурсу асинхронно из нескольких потоков. Например, Вы имеете на форме ListBox, в которые задумали выводить сообщения из обработчика события, который обрабатывается в другом потоке. У меня такое произошло, когда понадобилось отображение данных в ListBox, принятых в обработчике события поступления данных по USB.
Почему такая проблема возникает: ListBox создается в основном потоке (thread), запускающей GUI окна программы. Другие потоки могут быть созданы для поддержки обмена данными с внешними устройствами (сеть Ethernet, COM-порт, USB и т. п.). Эти другие потоки могут обрабатывать события приема данных асинхронно с выполнением основного потока. Поэтому если эти потоки попытаются что-то записать в ListBox, то это может привести к конфликту с записью в ListBox данных из основного потока GUI интерфейса программы.
Как проблему можно решить: для ListBox создаются подпрограммы (методы), через которые выводятся данные в ListBox. Данные выводятся в ListBox только через эти подпрограммы, больше никак. Эти подпрограммы делаются защищенными (thread-safe) для вызова из любых потоков через delegate, InvokeRequired, Invoke.
См. также: - справку в MSDN: Control.InvokeRequired - свойство, Control.Invoke - метод (Delegate), How to: Make Thread-Safe Calls to Windows Forms Controls - Making Windows Forms thread safe - Ссылки [9, 10] - Q101
В C# уже нельзя, как раньше, беспечно задавать константы операторами #define, и добавлять их h-файлы (как было принято в языках C и C++). Для C# нельзя использовать заголовки (файлы с расширением *.h или *.hpp), и директива #define не может использоваться для задания констант.
Для использования #define теперь такие правила:
1. Добавлять определения констант #define можно только в начале файла, иначе получите ошибку CS1032. 2. Директива define может использоваться только для задания ключей компиляции, но не для задания констант, иначе получите ошибку CS1025: Требуется однострочный комментарий или признак конца строки.
[Правила определения констант]
Константы в C# задаются как обычные переменные, только с помощью ключевого слова const:
constbyte USBCAN_SUCCESSFUL = 0x00;
Однако если Вы попытаетесь сделать это вне пространства имен класса, то получите ошибку CS0116: Пространство имен не может непосредственно содержать такие члены, как поля или методы. Константы нужно определять не только внутри скобок одной из секций namespace, но и в каком-то определенном классе. Например так (определение константы USBCAN_SUCCESSFUL):
Чтобы определить константы, которые видны в любом классе пространства имен приложения, нужно создать отдельный класс. Этот класс с константами обязательно должен быть размещен после кода класса формы, иначе конструктор формы не запустится - несмотря на то, что приложение компилируется без ошибок. Если попытаться запустить такое скомпилированное приложение, то возникнет ошибка MissingManifestResourceException (см. вопрос Q019).
Пример правильного определения глобальных констант в файле Form1.cs с помощью глобального статического класса CNST:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
namespaceMyApp
{
publicpartialclassForm1 : Form
{
publicForm1()
{
InitializeComponent();
}
На примере добавления события приема сообщения CAN (класс-обертка USBcanServer над функциями библиотеки UcanDotNET.dll для модулей USB CAN компании SYSTEC):
Для упрощенного добавления обработчика выполните следующие шаги:
- в коде Form1_Load напечатайте CANsrv. (в конце поставьте точку); - нажмите Ctrl+Space, из появившегося списка методов и событий выберите CanMsgReceivedEvent; - допечатайте пробел, + и =. Появится подсказка в виде текста: "new USBcanServer.CanMsgReceivedEventEventHandler(CANsrv_CanMsgReceivedEvent); (Нажмите клавишу TAB для вставки)" - нажмите клавишу TAB, и в коде автоматически будет создана строка:
Пример конвертации структуры tCanMsgStruct с языка C++:
typedefstruct _tCanMsgStruct {
DWORD m_dwID;// CAN Identifier
BYTE m_bFF;// CAN Frame format (BIT7=1: 29BitID / BIT6=1: RTR-Frame / BIT5=1: Tx echo)
BYTE m_bDLC;// CAN Data Length Code
BYTE m_bData[8];// CAN Data
DWORD m_dwTime;// Time in ms } tCanMsgStruct;
На C# будет так (только для компиляции в небезопасном режиме, с опцией /unsafe):
unsafepublicstruct tCanMsgStruct {
UInt32 m_dwID;// CAN Identifierbyte m_bFF;// CAN Frame format (BIT7=1: 29BitID / BIT6=1: RTR-Frame / BIT5=1: Tx echo)byte m_bDLC;// CAN Data Length Codefixedbyte m_bData[8];// CAN Databyte m_dwTime;// Time in ms }
Если код компилируется в безопасном режиме, то на C# структура tCanMsgStruct должна быть определена так:
Если не нужно точное соответствие положения полей в памяти, то структуры можно определять обычным способом, через ключевое слово struct. Такие структуры на C# почти что классы, и могут иметь даже собственные конструкторы, в которых можно инициализировать массивы, входящие в структуру как поле. Как и классы, в составе полей такие структуры могут иметь классы, атрибуты и методы. Пример структуры с конструктором:
publicstruct tFirmware {publicint minadr;publicint maxadr;publicbyte[] data;public tFirmware (int dummy){
minadr =65535;
maxadr =0;
data =newbyte[65536];//Заполнение массива данных файла байтами FF. Это соответствует чистому// состоянию памяти FLASH микроконтроллера AVR.for(int idx =0; idx < data.Length; idx++)
data[idx]= 0xFF;} }
Форматированный вывод в строку (то, что на C++ делает printf с параметрами форматирования %) в C# можно делать через метод Format, который есть у класса string. Тип форматирования зависит не только от строки формата, но и от типа выводимых параметров.
Строка формата задается теперь не символом %, а фигурными скобками. Первое число, которое идет после открывающей фигурной скобки, задают номер выводимого в строку параметра. Если после этого числа стоит двоеточие, то за ним идут символы до закрывающей фигурной скобки, которые определяют формат вывода параметра. Для того, чтобы понять, как пользоваться Format, проще рассмотреть несколько простых примеров.
[Целые десятичные числа]
int a = 1;
byte b = 2;
string tmptxt = String.Format("a={0} b={1} ", a, b);
[Целые числа в HEX-формат]
byte bval = 0xAA;
UInt16 u16 = 0x12AB;
//Если в формате применить маленькую букву x, то буквенные
// шестнадцатеричные цифры будут выводиться маленькими буквами:
Полностью аналогичного функционала static добиться нельзя. На C# невозможно создать внутри метода класса статическую переменную, чтобы она сохраняла свое значение между вызовами метода. Чтобы достичь этой цели, нужно объявить обычную глобальную переменную класса, и она будет видна не только этому методу класса, но и всем методам класса, и будет сохранять свое значение между вызовами метода.
На языке C# значение ключевого слова static поменялось. Это слово можно использовать только для глобальных переменных класса, и оно будет означать следующее - переменная класса, которая объявлена с ключевым словом static, будет иметь общее значение между всеми экземплярами этого класса. Т. е. она будет "глобальной" для всех экземпляров этого класса.
Подробнее про значение ключевого слова static на C# см. статью [7].
После запуска программы отобразятся сразу две формы, Form1 и Form2. Подробности см. в [4].
[Запуск формы действием пользователя]
Предположим, что нужно отобразить вторую форму только для какой-то цели, например для отображения лога. В этом случае нужно создать объект формы и вызвать его метод Show:
Проблема возникла после попытки добавить глобальный класс констант в пространство имен приложения (в данном примере calibr) перед классом определения формы. Так неправильно, при запуске приложения будет происходить ошибка MissingManifestResourceException, и конструктор формы не будет работать:
На языке C# переменное количество параметров передается функции в виде безразмерного массива. На примере класса log - в него добавлен метод write, которому можно передавать в виде параметров любое количество строк:
Ответ: изменять свойство Location нужно через вспомогательную переменную следующим образом:
...
Point tmpLocation =this.Location;
tmpLocation.X=1;
tmpLocation.Y=1;this.Location= tmpLocation;
...
Похожим образом нужно менять и размер окна формы, так как прямым присвоением Form.Size.Width и Form.Size.Height этого сделать нельзя (появится ошибка CS1612). Менять размер формы нужно так:
По умолчанию, когда Вы создаете ComboBox (путем перетаскивания его на форму с Панели элементов), свойство DropDownStyle установлено в значение DropDown. Совершенно неочевидно, что DropDown соответствует разрешенному редактированию текущего элемента. Чтобы запретить редактирование, измените свойство DropDownStyle на DropDownList.
Чтобы при запуске программы в ComboBox отображалось нужное значение из списка Items, программно поменяйте свойство SelectedIndex.
Несмотря на то, что ошибка расшифрована в MSDN, там не приводится метод её решения, и смысл ошибки на первый взгляд может быть не очевиден. Возможная причина ошибки - при объявлении класса [namespace.]класс1/тип пропущено ключевое слово public. Например, если дает ошибку следующее объявление класса:
public MyClass classVar =new MyClass();//тут выскакивает ошибка CS0052
namespace MyApp {class MyClass
{//далее определение класса
...
то попробуйте исправить определение класса MyClass, добавив public:
namespace MyApp {publicclass MyClass
{//далее определение класса
...
На языке C# модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту. Применение static к переменной класса означает, что эта переменная сохраняет свое значение для всех экземпляров этого класса. То есть эта переменная как бы общая для всех экземпляров этого класса. Например, если мы определим класс MyClass, и внутри него определим переменную static int cnt, то эту переменную можно использовать для подсчета созданных экземпляров класса MyClass, потому что переменная cnt будет иметь одно и то же значение для всех созданных экземпляров MyClass. Для C/C++ static имеет другое значение, потому что предназначено для сохранения значения переменной между вызовами функции, и для ограничения видимости переменной в пределах модуля.
Модификатор static можно использовать с классами, полями, методами, свойствами, операторами, событиями и конструкторами, но нельзя — с индексаторами, деструкторами или типами, отличными от классов. На статический член нельзя ссылаться через экземпляр, а можно только через имя типа. Подробности смотрите в MSDN, раздел "static (Справочник по C#)".
Такое может произойти, если рекурсивно закрывается одна форма из другой, в результате закрытие 1-й формы вызовет обработчик FormClosed 1-й формы, который вызовет обработчик FormClosed 2-й формы, который в свою очередь вызовет обработчик FormClosed 1-й формы, и так далее по циклу, пока стек приложения не переполнится. Например, Ваше приложение имеет несколько форм - Form1, Form2, Form3, и нужно, чтобы закрытие одной формы привело к закрытию всего приложения. Если Вы напишете код, который будет закрывать все формы подряд в каждом обработчике FormClosed, то это приведет к вышеуказанной ошибке. Вот пример ошибочного кода (Form1 главная форма, которая запускается методом Application.Run):
А вот так нужно исправить код, чтобы не возникала ошибка System.StackOverflowException в System.Windows.Forms.dll:
privatevoid Form1_FormClosed(object sender, FormClosedEventArgs e){//тут действия, выполняемые при закрытии приложения
...
}privatevoid Form2_FormClosed(object sender, FormClosedEventArgs e)
{//закрытие Form1 завершит программу, т. е. будут автоматически закрыты Form1, и Form3
Program.Form1.Close();}privatevoid Form3_FormClosed(object sender, FormClosedEventArgs e){ //закрытие Form1 завершит программу, т. е. будут автоматически закрыты Form1, и Form2
Program.Form1.Close();}
В выражении частное и делитель имеют разные, несовместимые типы. Попробуйте и частное, и делитель привести к типу float, для этого добавьте префикс (float), который обеспечит явное приведение типа переменной.
float var1, result; int var2;
...
result = var1 / var2;//ошибка CS0019
result = var1 /(float)var2;//OK
Нужно во время разработки задать у формы свойства MinimumSize и MaximumSize на нужные значения, и сделайте их одинаковыми. Также задайте свойство формы MaximizeBox в значение false. После этого пользователь, запустив программу, не сможет поменять размер окна, и не сможет развернуть окно на весь экран.
Второй способ зафиксировать размер формы во время выполнения - задайте обработчик события OnResize, в котором установите свойства Width и Height в нужное значение.
Решить проблему можно, если отключить для отладки возможность IntelliTrace. Делается это следующим образом - меню Сервис -> пункт Параметры -> разверните узел IntelliTrace -> щелкните Общие -> снимите флажок Включить IntelliTrace -> нажмите кнопку OK.
В программе могут быть и невизуальные классы, у которых нет окон, т. е. во время разработки к ним не привязана форма Form. Поэтому бросить компонент с панели элементов в такой класс не получится.
Однако можно создать экземпляр класса (компонент) во время выполнения программы, что решит проблему. На примере таймера Timer с Панели Элементов, который добавляется программно в невизуальный класс MyClass:
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq;usingSystem.Text; namespace MyApp {class MyClass
{
...
//переменная - хранилище для экземпляра класса таймераprivateSystem.Windows.Forms.Timer mytimer;//счетчик времени в единицах 100 мсpublic UInt32 time100stamp =0;/////////////////////////////////////////////////////////////////// Конструктор класса MyClasspublic MyClass(){
...
//создание экземпляра класса таймера во время выполнения
mytimer =newSystem.Windows.Forms.Timer();//интервал таймера 100 мс
mytimer.Interval=100;//добавление события на срабатывание таймера
mytimer.Tick+=newSystem.EventHandler(this.mytimer_Tick);//запуск таймера
mytimer.Start();
...
}/////////////////////////////////////////////////////////////////// Событие 100-мс таймера, которое просто инкрементирует/// счетчик времени.privatevoid mytimer_Tick(object sender, EventArgs e){
time100stamp++;}
...
} }
//Как получить имя исполняемого файла без расширения? usingSystem.IO; string fullname =System.Reflection.Assembly.GetExecutingAssembly().Location; string name_without_ext = Path.GetFileNameWithoutExtension(fullname);
Если цикл длинный, то программа начинает заметно тормозить как саму себя (интерфейс программы работает плохим откликом), так и компьютер. Если Вы запустили отдельный поток для каких-нибудь действий (например, ждете данные от сокета, или последовательного порта), то цикл без задержки нежелателен, нужно отдавать ресурсы процессора другим потокам и программам. Делается такая операция обычно с помощью Sleep(ms). Пример введения задержки (например, для приостановки цикла) на 10 миллисекунд:
Ошибка 17 Не удалось опубликовать из-за того, что не удалось построить проект. 1 Ошибка 18 При подписи произошла ошибка: Не удалось подписать bin\Release\app.publish\\setup.exe. SignTool Error: The signer's certificate is not valid for signing. SignTool Error: An error occurred while attempting to sign: bin\Release\app.publish\\setup.exe C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets 3987
Решение проблемы: зайдите в меню Проект -> Свойства проекта, снимите галочку "Подписать манифесты ClickOnce". После запуска публикации система выдаст запрос: "Приложение подписано не тем ключом, что существующее на сервере приложение. Перезаписать его?", ответьте утвердительно.
Известно, что объединение (union) на языках C и C++ позволяет интерпретировать одни и те же данные по-разному. Например, 32-разрядные данные (int) можно представить как два 16-разрядных слова (short) или как 4 отдельные байта (unsigned char).
К сожалению, C# не поддерживает объединения C/C++. Однако с той же целью можно использовать StructLayout(LayoutKind.Explicit) и атрибуты FieldOffset. Предположим, что у Вас есть структура из 64 бит данных, и Вам нужно представить её либо как 8 байт (byte), либо как два целых 32-разрядных числа (int). Атрибут FieldOffset регулирует смещение нужных нам частей данных. Например:
usingSystem.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] struct MyArray {//здесь начинаются 8 байт данных[FieldOffset(0)]publicbyte Byte1;[FieldOffset(1)]publicbyte Byte2;[FieldOffset(2)]publicbyte Byte3;[FieldOffset(3)]publicbyte Byte4;[FieldOffset(4)]publicbyte Byte5;[FieldOffset(5)]publicbyte Byte6;[FieldOffset(6)]publicbyte Byte7;[FieldOffset(7)]publicbyte Byte8;//здесь те же 8 байт интерпретируются как два 32-битные числа[FieldOffset(0)]publicint Int1;[FieldOffset(4)]publicint Int2; }
Решение проблемы: по умолчанию перечисление (enum) на C# подразумевает использование целых чисел (int). Чтобы исправить ошибку, добавьте явное указание типа для перечисления (можно указать типы byte, sbyte, short, ushort, int, uint, long или ulong):
Для сравнения используйте метод String.Compare. Он возвращает целое число, которое показывает их относительное положение в порядке сортировки. Результат равен нулю, если сравниваемые строки одинаковы. Простые примеры:
if(0==String.Compare(strA, strB, true)) {//строки strA и strB совпадают без учета регистра
.. }if(0==String.Compare(strA, strB)) {//Сюда попали, если строки равны по критерию // сортировки. Внимание: на сравнение при таком // вызове влияют правила учета регистра, связанные// с языком и региональными параметрами.//Поэтому этот вариант перегрузки String.Compare// использовать нежелательно.
.. }
(Даниил Захаров): Деструктор в С# несколько отличается от деструктора в С++.
В С++ он вызывается напрямую механизмами языка. Это происходит так же быстро, как и вызов метода из кода пользователя (ну, пожалуй, совсем ненамного дольше).
В С# деструкторы вызываются не пользовательским кодом, а средой CLR. Код C# потому и называется управляемым, что по сути он запускается под управлением среды. Поэтому, к примеру, здесь нет указателей - среда сама выполняет операции с указателями автоматически и не поддерживает код, который их использует. Механизм использования указателей все же оставлен в целях совместимости, но при этом классы, их использующие, надо помечать ключевым словом unsafe, разрешать выполнение этого unsafe кода в свойствах проекта. Но самое главное - среда не будет управлять этим кодом - она передаст ему управление и будет дожидаться, когда получит его обратно.
То же самое относится и к деструкторам. Деструкторы призваны освободить ресурсы, занимаемые классом. Но класс не может освободить ресурсы, которыми фактически не владеет. А по факту владеет он только теми кусками, которыми управляет сам. Поэтому деструктор вызывается не пользовательским кодом, а из CLR, то есть снаружи. Но не сразу, а только после того, как среда отметит, что на экземпляр класса больше не ссылается ни одной переменной. Отсюда возникает задержка - только после второго прохода сборщика мусора. И отсюда потеря производительности - мы заставляем сборщик мусора отслеживать еще и деструкторы.
Моя практика показала, что при уничтожении около полусотни объектов с деструкторами (причем до крайности простыми - из статической переменной вычиталась единица) комп "подвисает" так, что это становится заметно пользователю. Замеры показали диапазоны 18 .. 47 мс, т. е. почти всегда "терялся" один кадр на экране, а в некоторых случаях - до двух/трех (при частоте смены кадров в 30 Гц).
Использование директивы using объявляет пространство поиска используемых имен в библиотеках C#, что позволяет упростить и сократить текст программы. Для примера - использование using System.IO позволяет обойтись без постоянного упоминания префикса System.IO:
//Без using System.IO:
.. if(!System.IO.File.Exists("readme.txt")) {//.. } //С использованием using System.IO: usingSystem.IO;
.. if(!File.Exists("readme.txt")) {//.. }
В консольном и GUI-приложении можно создать и отобразить окно с помощью класса MessageBox. В этом окне можно отобразить текст, кнопки и символы, которые информируют пользователя о чем-то и/или дают ему указания. При этом работа приложения блокируется до тех пор, пока пользователь не закроет окно (стандартная особенность модального окна). Более подробно об использовании MessageBox, о его свойствах и методах см. справку MSDN (запрос для Google MessageBox site:msdn.microsoft.com). Простейший пример вызова MessageBox:
Если при попытке компиляции выводится ошибка о том, что System.Windows.Forms не найдено в пространстве имен ('Forms' does not exist in the namespace system.windows), то добавьте на него ссылку в проекте. Для этого сделайте правый клик на дерево файлов проекта (Solution Tree), выберите в контекстном меню Добавить ссылку..., в появившемся окне "Добавить ссылку" перейдите на закладку .NET, прокрутите список и выберите в нем System.Windows.Forms, нажмите OK.
Еще один пример подпрограммы вывода модального диалогового окна с выходом из приложения, если установлен в true параметр exit:
При подписи произошла ошибка: Не удалось подписать binReleaseapp.publishsetup.exe.
SignTool Error: The signer's certificate is not valid for signing.
SignTool Error: An error occurred while attempting to sign: binReleaseapp.publishsetup.exe имя_проекта
Как исправить: зайдите в свойства проекта (Project -> Properties...), перейдите в раздел Подписывание (Signing), нажмите кнопку "Создать тестовый сертификат..." (create test certificate). Появится окно запроса пароля сертификата. Оставьте поля пустыми и нажмите OK, после чего опубликуйте проект.
1. Откройте Панель элементов, выберите PictureBox, бросьте на форму. 2. Выберите свойства pictureBox1 -> BackgroundImage, нажмите справа кнопку с многоточием, откроется окно выбора ресурса. Выберите уже имеющийся ресурс, или нажмите внизу кнопку Импорт..., и выберите нужную картинку (формата BMP или PNG). 3. По умолчанию свойство BackgroungImageLayout установлено в Tile, что означает, что картинка будет размножена мозаикой по всей площади pictureBox1 (границы этой площади можно раздвинуть мышкой). Чтобы картинка была только одна, выберите значение None для свойства BackgroungImageLayout. Также свойство SizeMode установите в AutoSize, если хотите, чтобы размеры pictureBox1 автоматически подстраивались под размер картинки. 4. Чтобы поменять картинку runtime (во время выполнения программы), нужно присвоить свойство BackgroundImage, пример:
В VB такая операция происходит присвоением object=nothing, на C++ применяется оператор delete. Как явно удалять объекты на C#?
В C# можно сделать аналогичное действие object=null, но в действительности присвоение object=null не удаляет объект, а просто делает недействительной ссылку на него. В .NET это дает сигнал сборщику мусора (garbage collector) удалить объект из памяти. Но Вы не можете знать, когда конкретно это произойдет.
В C# при создании объекта оператором new память для него выделяется из кучи (heap). Вы также можете Объект удалится автоматически тем же сборщиком мусора, когда управление выйдет за пределы существование объекта (например, когда завершается функция, которая создала объект оператором new). Но опять-таки неизвестно, в какой момент произойдет освобождение памяти, связанное с удалением объекта.
Если в C# у объекта есть метод Dispose(), то его нужно обязательно вызвать до того, как управление выйдет из сферы действия объекта. Вы можете использовать ключевое слово using, чтобы сделать эту операцию автоматически, например:
using (Graphics gr = Graphics.FromBitmap(bm))
{
// тут какие-то действия
...
}// здесь будет автоматически вызван gr.Dispose()
Принудительно запустить процедуру сбора мусора можно вызовом GC.Collect(), но это делать не рекомендуется.
Type myType = typeof(MyClass); // Получение строки namespace класса myClass.
Console.WriteLine("Namespace: {0}.", myType.Namespace);
Для строки:
Type myType = typeof(MyClass); var n = myType.Namespace;
Для метки WinForm:
Type myType = typeof(MyClass);
namespaceLabel.Text = myType.Namespace;
Если у Вас есть экземпляр x класса A внутри namespace B, то можете использовать:
string s = x.GetType().Namespace;
В строке s не будет содержаться "B". Вы можете также использовать x.GetType().Name, чтобы получить имя типа, или x.GetType().FullName чтобы получить полное имя, вместе с B.
Чтобы в Windows Forms нарисовать метку с вертикальным текстом, нужно переопределить событие Paint, и рисовать в нем метку вертикально. Имейте в виду, что GDI оптимизировано для горизонтального рисования меток. Если Вы повернете текст (даже если угол поворота будет кратен 90 градусов), то возможно это будет работать не очень качественно.
Возможно лучшим решением будет нарисовать текст (или поручить метке нарисовать себя) в растровую картинку (bitmap), и затем отобразить эту картинку повернутой.
Ниже приведен код, который рисует Custom Control с вертикальным текстом. Имейте в виду, что текст ClearType не работает, если текст не горизонтальный.
Можно повернуть текст в событии OnPaint, или в методе Paint:
privatevoiduc1_Paint(object sender, PaintEventArgs e)
{
string Name;
var g = e.Graphics;
g.DrawString(Name, new Font("Tahoma", 8), Brushes.Black, 0, 0,
newStringFormat(StringFormatFlags.DirectionVertical));
}
[Вариант 2]
Создайте свой класс myLabel, который может поворачивать текст. Этот класс можно будет перетаскивать в форму приложения прямо из панели компонентов ToolBox.
'Необработанное исключение типа "System.BadImageFormatException" произошло в имя_приложения.exe
Дополнительные сведения: Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0xXXXXXXXX)'
Чаще всего проблема из-за того, что вызов функции (метода) потребовал вызова DLL, скомпилированной под другую систему. Например, программа работает на 64-битной версии Windows, а DLL сделана для 32-битной версии. Что можно сделать:
1. Найти DLL, которая скомпилирована под Вашу операционную систему.
2. Если у Вас 64-битная система, а загружаемая DLL 32-битная, то можно изменить в свойствах тип конечной платформы на 32-битную. Для этого откройте свойства проекта, перейдите в раздел Построение, и выберите в выпадающем списке "Конечная платформа:" вариант x86 (см. скриншот ниже). После этого пересоберите проект.
Для этого достаточно поменять свойство формы Text. Пример:
[Текст модуля myform.cs]
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
namespacemyprog
{
/// < summary >/// Главная форма/окно программы./// < /summary >publicpartialclassmyform : Form
{
...
privatevoidUpdateForm(bool force_update)
{
//рисование заголовка формы
Text = "Это будет новый текст на плашке формы";
...
}
...
Можно также к свойству Text обращаться через объект this:
this.Text = "Это будет новый текст на плашке формы";
После этого на форме будет отображаться стандартная кнопка с крестиком, но она перестанет действовать. Подробнее про поведение события закрытия формы см. [6].
Можно ли в C# декларировать переменную таким образом, чтобы в каждом классе (представленном модулем *.cs) можно было получить доступ к содержимому этой переменной без необходимости создавать экземпляр какого-либо класса? Другими словами, если ли в C# глобальные переменные, не привязанные к какому-либо экземпляру класса? Есть ли какая-то альтернатива обмениваться данными между классами через одну внешнюю переменную?
В C# не существует такого понятия, как глобальная переменная, и точка. Однако есть некий аналог в виде модификатора public static, который можно применять к классам и к отдельным членам класса. Короче говоря, используйте класс типа public static, и получайте доступ к нему где угодно и откуда угодно.
publicstaticclassGlobals
{
publicstatic String s_Name = "Mike"; // Эту переменную можно модифицировать в кодеpublicconst Int32 VALUE = 10; // Эту нельзя
}
Можно использовать переменные этого класса в любом месте, когда они находятся в одном и том же пространстве имен (namespace), что и ссылающийся на переменные код. Можно объявить MyStaticValues или Globals без пространства имен (что поместит эти статические классы в глобальное пространство имен приложения, application namespace), или Вы работаете с разными пространствами имен, то можете указать нужное пространство следующей директивой:
string name = Globals.s_Name;
[Как использовать аналоги глобальных переменных в C#]
Сначала хорошенько подумайте - действительно ли Вам нужна глобальная переменная. Скорее всего решение должно быть принято, исходя из рассмотрения техзадания на разрабатываемое программное обеспечение. Обычно статические переменные могут пригодиться для таких объектов в программе, которые имеются исключительно в одном экземпляре, их не нужно создавать и удалять динамически, и если эти объекты подразумеваются для использования несколькими разными классами в программе. Имейте в виду, что иногда проблемы, возникающие из-за конкуренции доступа к глобальным переменным (особенно актуально в сложных системах), бывает трудно отладить, поэтому будьте готовы к трудностям.
Итак, если положительное решение принято и глобальные переменные все-таки нужны, то действуйте примерно по такой схеме:
1. Поместите все глобальные переменные в один static класс (для удобства управляемости кодом). 2. Используйте внутри этого класса обертки get/set для доступа к "глобальным" переменным. Это предоставит дополнительный механизм для отладки проблем, возникающих из-за конкурентного доступа.
Вот базовый пример для такого класса:
publicclassGlobals
{
privatestaticbool _expired;
publicstaticbool Expired
{
get
{
// Чтение обычно очень простое:return _expired;
}
set
{
// В этом месте можно добавить логику, отслеживающую// проблемы конкурентного доступа (race conditions),// вывод в лог или другие методы отладки.
_expired = value;
}
}
// По ситуации можно расширить этот пример static-методами Read-Modify-Write// (чтение-модификация запись) для поддержки целостности данных при // конкурентном доступе (поддержка атомарности). Конкретное решение// зависит от Ваших потребностей.
...
}
Использование переменных этого класса из других классов (находящихся в том же пространстве имен namespace) может быть таким:
// Чтение:
bool areWeAlive = Globals.Expired;
// Запись:
Globals.Expired = true;
Хорошая информация по теме static классов C# есть в статье "Статика в C#", опубликованной на Хабрахабр (ключевые слова для поиска Статика в C# site:habrahabr.ru).
Пример определения дочернего класса TMyClass внутри родительского класса myform:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Runtime.InteropServices;
namespacemyprog
{
publicpartialclassmyform : Form
{
publicclassTMyClass
{
publicTMyClass()
{
//Это конструктор дочернего класса
...
}
...
} //Определение массива классов из 7 элементов:public TMyClass [] classarray = new TMyClass [7];
...
publicmyform()
{
//Это конструктор родительского класса
InitializeComponent();
...
}
//Вот так нужно правильно инициализировать массив классов TMyClass внутри // события загрузки формы (родительский класс myform). Альтернативно то же// самое можно выполнить в конструкторе родительского класса:privatevoidmyform_Load(object sender, EventArgs e)
{
for (int idx = 0; idx < 7; idx++)
{
//Вызов конструкторов TMyClass:
classarray[idx] = new TMyClass();
}
}
}
}
После этого можно обращаться к экземплярам классов TMyClass по индексу (вызывать его методы, обращаться к свойствам). Если не сделать инициализацию элементов массива, то при запуске программы и попытке доступа к экземплярам классов из массива получите ошибку "ссылка на объект не указывает на экземпляр объекта".
У меня была следующая проблема: KeyDown срабатывает нормально, пока не добавлена на форму кнопка. Как только кнопка добавлена, KeyDown перестает работать.
[Решение 1]
Нужно установить у формы свойство KeyPreview=true. Этот совет можно найти на большинстве форумов, однако он помогает не всегда - возможно, это зависит от версии библиотек Visual Studio C#. Мне не помогло (Microsoft Visual Studio 2010 Версия 10.0.40219.1 SP1Rel).
[Решение 2]
Нужно переопределить метод ProcessCmdKey формы. Для этого просто добавьте в класс формы следующий код:
protectedoverrideboolProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Space:
//Действия при нажатии пробела:
...
returntrue;
case Keys.Left:
//Действия при нажатии кнопки "влево":
...
returntrue;
case Keys.Right:
//Действия при нажатии кнопки "вправо":
...
returntrue;
//Тут можно добавить обработку других нажатий...
...
}
returnbase.ProcessCmdKey(ref msg, keyData);
}
Обратите внимание, что при возврате true из метода ProcessCmdKey не будет отправлено событие нажатия на элемент управления, на котором установлен фокус.
Окно имеет высоту (свойство Height) и ширину (свойство Width), которые определяют размеры окна формы. Эти свойства можно читать или менять программно. Однако есть еще и "клиентская" область формы, которая меньше. По ширине она меньше на ширину бордюров окна, а по высоте она меньше еще и на размер верхней плашки (title bar).
[Свойство ClientSize]
У свойства ClientSize формы есть поля только для чтения - Width и Height, по значению которых можно определить размеры клиентской области формы (ширину и высоту соответственно).
[Метод SetClientSizeCore]
Метод SetClientSizeCore принимает 2 параметра - x и y, и позволяет установить размеры клиентской области формы.
2. Вместо вложенных классов (nesting) применить наследование (inheritance). Родительский класс должен иметь тип доступа public, чтобы можно было применить наследование:
publicclassmyform : Form
{
publicint timestamp;
...
}
При попытке обновить содержимое полосы статуса приложения Windows Forms ничего не происходит. Вот пример кода, который по замыслу должен показывать текст, но на самом деле текст не отображается:
Свойство Text класс StatusStrip получает при наследовании из класса ToolStrip (который в свою очередь наследует его из класса Control). Но у свойства Text класса ToolStrip нет визуального эффекта, просто так почему-то спроектирован класс ToolStrip. Поэтому присваивать значение свойству Text класса StatusStrip бесполезно. Поначалу это может запутать.
Для того, чтобы на самом деле вывести текст в строку статуса StatusStrip, необходимо добавить к StatusStrip элемент StatusLabel. В дизайнере формы это делается следующим образом, см. скриншот:
По умолчанию будет создан элемент класса toolStripStatusLabel1, доступный напрямую из кода формы. Для отображения текста нужно менять его свойство Text, при этом метод Refresh для StatusStrip вызывать не обязательно. Пример:
namespacemyApp
{
publicpartialclassForm1 : Form
{
...
// Создаст и загрузит INI-файл в той же папке, где находися исполняемый файл.
// INI-файл получит имя EXE.ini (здесь EXE это имя исполняемого файла до
// расширения *.exe):
var ini = new IniFile();
// Или укажите определенное имя в текущей директории:
var ini = new IniFile("Settings.ini");
// Или укажите определенное имя в определенной директории:
var ini = new IniFile(@"C:\Settings.ini");
В результате Вы получите объект IniFile, после чего можете воспользоваться его методами Read, Write, KeyExists, DeleteKey, DeleteSection.
[Запись парамеров в ini-файл]
Предположим, нам нужно получить INI-файл такого вида:
[SECTION]
MyPath=c:\temp
Для этого можно использовать следующий код:
ini.Write("MyPath", "c:\\temp", "SECTION");
Если не указывать секцию (последний параметр), то будет автоматически создана секция INI-файла с именем namespace приложения (в данном примере namespace MyApp).
ini.Write("MyPath", "c:\\temp");
[Чтение параметров из ini-файла]
Пример чтения параметра MyPath из секции SECTION:
string strPath = ini.Read("MyPath", "SECTION");
Если не указывать имя секции (последний параметр), то в качестве имени секции будет использоваться имя namespace приложения (в данном примере namespace MyApp).
string strPath = ini.Read("MyPath");
[Проверка существования параметра]
Если параметр MyPath не существует в секции SECTION, то он будет создан и записан:
Пусть у нас есть вот такой файл, таблица частот (test.xml):
<?xml version="1.0" encoding="Windows-1251"?><config>
# K - частота, Гц
# V - уровень напряжения, V
<OBJECTK="10"V="0.2"/><OBJECTK="50"V="3.30"/><OBJECTK="1476"V="12"/></config>
Ниже показан пример загрузки этого файла в таблицу dataGridView1. Для использования этого кода добавьте в начало модуля директиву using System.Xml.
string fromfile = "xml\\test.xml";
XmlDocument xDoc = new XmlDocument();
try
{
// Загрузка XML-файла:
xDoc.Load(fromfile);
// Получение корневого элемента (в нашем примере это config):
XmlElement xRoot = xDoc.DocumentElement;
// Очистка таблицы:
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
// Отображение имени файла на плашке Form1:this.Text = fromfile;
//Заполнение dataGridView1 частотами из XML:foreach (XmlNode xnode in xRoot)
{
// Цикл по элементам OBJECT, каждый такой элемент соответствует// одной строке в таблице:if ("OBJECT" == xnode.Name)
{
DataGridViewRow row = (DataGridViewRow)dataGridView1.Rows[0].Clone();
row.HeaderCell.Value = String.Format("{0}", dataGridView1.RowCount);
// Цикл по столбцам строки, считывание параметров K и V:for (int i = 0; i < xnode.Attributes.Count; i++)
{
if (i < 2)
row.Cells[i].Value = xnode.Attributes[i].Value.ToString();
}
// Добавление строки:
dataGridView1.Rows.Add(row);
dataGridView1.Refresh();
}
}
}
Важное замечание: у метода Load (filename) объекта XmlDocument есть особенность поведения под Windows XP, если в качестве filename передавать не полный, а относительный путь. По крайней мере этот глюк у меня наблюдался на Visual Studio 2010 (.NET Framework версии 4.6.01590 SP1Rel), возможно в последующих версиях это исправлено. Проблема заключается в следующем: в Windows XP первая передача в Load относительного имени файла происходит успешно, но этот каталог запоминается. Все последующие вызовы Load с передачей относительного имени заканчиваются неудачей, потому что к файл ищется по пути, полученному сложением текущего каталога с переданным относительным путем. Под Windows 7 такой проблемы нет, относительные пути всегда отрабатывают без проблем.
Для обхода этой ошибки приходится в метод Load передавать полное имя файла, тогда все нормально работает и под Windows XP, и под Windows 7:
ToolStripComboBox это выпадающий список выбора, который можно встроить как элемент меню.
[Как динамически добавлять элементы списка]
ToolStripComboBox MyMenuItemToolStripComboBox = new System.Windows.Forms.ToolStripComboBox();
for (int i = 0; i < 10; i++)
{
MyMenuItemToolStripComboBox.Items.Add("COM" + i.ToString());
}
[Как запретить редактирование элементов списка]
ToolStripComboBox MyMenuItemToolStripComboBox = new System.Windows.Forms.ToolStripComboBox();
... //тут могут быть добавлены элементы списка ToolStripComboBox
MyMenuItemToolStripComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
[Текущий элемент списка]
Текущий элемент списка ToolStripComboBox доступен на чтение и запись через свойство SelectedIndex. Нумерация элементов начинается с нуля.
Как проверять выбранный пользователем элемент списка:
1. Добавьте файл, который нужно устанавливать вместе с приложением, в дерево файлов проекта (Обозреватель решений). Для этого можно просто перетащить файл в корневую папку проекта, отображаемую в Обозревателе решений.
2. Измените свойство файла "Действие при построении" в значение "Содержание" (по умолчанию там указано "Нет").
3. Откройте свойства проекта, зайдите в раздел "Публикация". Кликните на кнопку "Файлы приложения...". Убедитесь, что нужный файл был добавлен в список таблицы публикуемых файлов. В строке таблицы этого файла должны быть установлены следующие значения:
Состояние публикации: Включить (Авто) Группа загрузки: (Требуется) Хеш: Включить
Так же настраивается публикации папки вместе с содержащимися в ней файлами. Перетащите публикуемую папку в Обозреватель решений и повторите шаги 2 и 3. На шаге 3 следует проверить, что в таблице публикуемых файлов находятся все файлы, содержащиеся в публикуемой папке.
После попытки запуска инсталлятора (setup.exe) приложение не устанавливается, в логе установки выводится сообщение:
Указанная сборка не установлена в системе. (Исключение из HRESULT: 0x800736B3)
или:
Отказано в доступе. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))
Решение проблемы:
1. Удалите папку 2.0, которая находится в профиле пользователя. На Windows 7 и Windows Vista эта папка находится в каталоге C:\Users\имя_пользователя\AppData\Local\Apps\. На Windows XP эта папка находится в каталоге c:\Documents & Settings\имя_пользователя\LocalSettings\Apps\. При удалении папки могут потребоваться права администратора. Если удалить папку не получается, то перезагрузите компьютер, закройте все приложения и с правами администратора повторите попытку удаления папки 2.0.
2. Если после удаления папки 2.0 проблема не исчезла, то запустите с правами администратора команду:
На примере библиотек .NET Ivi.Visa и NationalInstruments.Visa:
"Не удалось найти имя типа или пространства имен "Ivi" (пропущена директива using или ссылка на сборку?)" "Не удалось найти имя типа или пространства имен "NationalInstruments" (пропущена директива using или ссылка на сборку?)"
Разверните в Обозревателе решений папки проекта, выполните правый клик на папке "Ссылки", выберите "Добавить ссылку...". Откроется окно диалога выбора библиотек. На закладке .NET найдите нужные библиотеки (в нашем примере это Ivi.Visa Assembly и National Instruments VISA, можно выбирать несколько библиотек сразу, удерживая клавишу Ctrl) и кликните OK.
В папке "Ссылки" появятся новые библиотеки, и ошибка "Не удалось найти ..." исчезнет.
Необходимо было в момент запуска программы заполнить выпадающий список ComboBox нужными значениями (имена COM-портов) и потом выбрать из этого списка нужный, чтобы он по умолчанию отображался после полной загрузки программы. Все это я делал в обработчике события загрузки формы Form1_Load. Однако в при попытке присвоить значение свойству SelectedIndex срабатывало исключение типа "System.NullReferenceException": Ссылка на объект не указывает на экземпляр объекта (Object reference not set to an instance of an object).
Причина ошибки в том, что в момент загрузки формы визуальные элементы еще не полностью инициализированы. Решение проблемы: устанавливать SelectedIndex с помощью вызова события SelectedIndexChanged:
privateint desiredidx = -1;
privatevoidForm1_Load(object sender, EventArgs e)
{
...
if (-1 != desiredidx)
MyComboBox_SelectedIndexChanged(MyComboBox, new EventArgs());
...
В состав Visual Studio 2010 входит простой, но иногда довольно неудобный способ для создания дистрибутива (публикации) приложения C#. Это так называемый инструментарий ClickOnce (доступен из меню Построение -> Опубликовать). Такой способ создания дистрибутива всем хорош, но его основное неудобство заключается в том, что невозможно никаким способом задать место установки приложения на компьютере пользователя. Приложение установится автоматически в папку, находящуюся в пределах профиля пользователя (так называемый кэш приложения, Application Cache). Путь до каталога установки приложения получается очень длинным и сложным, на Windows 7 и Windows 10 это будет что-то наподобие C:\ Users\ имя_пользователя\ AppData\ Local\ Apps\ 2.0\ 9HOERDLL.1LY\ KOHZZR9V.QHE\ cali..tion_e314c8db98ca16b5_0001.0000_1b1f3d3fa0feae4c\, или на старых компьютерах с Windows XP это папка c:\ Document and Settings\ имя_пользователя\..
Причина такого решения в том, что решаются многие проблемы безопасности - для установки и работы приложения не требуется наличие прав администратора. Т. е. пользователь с пониженными правами сам может установить, удалить или обновить приложение, вмешательство администратора не требуется. Пользователь с пониженными правами и приложения, работающие от его имени (в том числе трояны и вирусы), никак не могут нарушить содержимое таких системных папок, как %SystemRoot%, %ProgramFiles%, %ProgramFiles(x86)%, %ProgramData% и т. п.
За все надо платить - выигрываем в безопасности и в простоте, проигрываем в прозрачности и в том, чтобы применять совсем другие способы для хранения получения дисковых данных, которые не привязываются к полному пути на диске. К тому же если на компьютере с приложением работает несколько пользователей с одним и тем же приложением, установленным через ClickOnce, то получается несколько копий приложения.
Недостатки ClickOnce следующие:
• При установке очень сложный путь до места установки приложения. • При обновлении приложения его место расположения на диске постоянно меняется. • Несколько копий одного и того же приложения на диске, если его установили несколько разных пользователей компьютера.
Таким образом, если Вас не устраивает установка ClickOnce, то нет никакого другого выхода, как инсталляция путем ручного копирования скомпилированного приложения в нужную папку, или создание дистрибутива другим способом, например через создание пакета *.msi (Microsoft Windows Installer).
См. также:
• Обзор установки приложения через ClickOnce • Where do I put my data to keep it safe from ClickOnce updates? site:robindotnet.wordpress.com • Create Setup and Deployment Project in Visual Studio 2008/2010 site:c-sharpcorner.com • Deploying WPF Application with ClickOnce Deployment Techniques site:c-sharpcorner.com
Идею взял отсюда: Автоматическая прокрутка в конец TextBox при добавлении текста site:devnuances.com. Принцип простой - нужно взять длину текста из свойства Text.Length, и затем переставить позицию каретки (курсора ввода) в конец этого текста. Удобнее всего это делать в обработчике события TextChanged экземпляра объекта RichTextBox. Код обработчика:
Имейте в виду: чтобы автоматическая прокрутка при добавлении текста работала, TextBox должен получить фокус (можно реализовать вызовом метода Focus). Пример:
Иногда нужно удалить из конца строки символы возврата каретки (CR, 0x0d, '\r') и перевода строки (LF, 0x0a, '\n'). Проще всего это реализовать с помощью метода TrimEnd. В качестве параметра он принимает список символов, которые нужно удалить в конце строки. Пример:
Пример такого сообщения: 'Не удалось найти тип "UsbLibrary.UsbHidPort". Проверьте, что есть ссылка на сборку, содержащую этот тип. Если этот тип является частью разрабатываемого проекта, убедитесь в том, что проект успешно скомпилирован с использованием параметров вашей текущей платформы для любого процессора.
Содержимое стека и номер строки для этой ошибки недоступны.'
При этом сам проект нормально компилируется, но открыть форму в режиме визуального конструктора не получается, что создает определенные неудобства.
Чаще всего подобная ошибка происходит при перемещении модулей кода или библиотек в проекте, когда в файле *.pdb осталась ссылка на старое размещение файла на диске. Шаги для устранения ошибки:
1. Сделайте резервную копию всех файлов проекта.
2. Для устранения ошибки чаще всего достаточно удалить файл *.pdb, он содержится в подкаталоге Debug или Release проекта, в зависимости от выбранной конфигурации. Закройте Visual Studio, удалите файл *.pdb, и заново откройте Visual Studio.
3. Если ошибка не исчезла, то удалите в тексте кода формы (обычно это файл Form1.Designer.cs) все ссылки на используемый класс (в данном примере UsbHidPort) и библиотеку (в данном примере UsbLibrary), после чего форма в конструкторе начнет нормально открываться. Затем заново добавьте используемую библиотеку в проект, и добавьте обратно в код формы строчки, которые Вы удалили.
Учет реального времени часто требуется для обслуживания таймаутов, например для процесса обмена данными через USB или COM-порт. Обычно более-менее сложное приложение всегда многопоточное, и поэтому отслеживать прошедшее реальное время простыми вызовами Sleep (см. Q040) не получится.
Есть несколько способов отсчитывать глобальное время. Мне больше всего нравится делать это с помощью тиков операционной системы Windows. В библиотеке C# .NET для этого есть удобное свойство Now.Ticks структуры System.DataTime. Значение Ticks будет содержать глобальное, постоянно увеличивающееся реальное время (64-битное целое число), выраженное в тиках, относительно момента времени 0 часов 0 минут 0 секунд 1 января 0001 года. Каждый тик соответствует 100 наносекунд. Таким образом, чтобы получить абсолютное время в микросекундах, нужно Ticks поделить на 10, а чтобы получить время в миллисекундах, нужно поделить Ticks на 10000. И конечно, абсолютное время в секундах будет равно Ticks/10000000.
Если мы можем в любой момент получить абсолютное текущее время, то отсчет таймаута становится простым. Достаточно запомнить Ticks в момент начала отсчета таймаута, и потом постоянно проверять текущее значение Ticks на разницу с начальным запомненным значением. Как только разница между этими двумя значениями превысит заданный порог, то можно считать, что произошел таймаут.
Можно также использовать другой похожий способ - задать сначала значение счетчика таймаута, и потом в каждой итерации проверки отнимать от счетчика прошедшее абсолютное время. Таймаут истек, когда счетчик дошел до нуля. Ниже приведен простой пример такой обработки таймаута обмена через последовательный порт с помощью DataTime.Ticks в потоке BackgroundWorker.
#region Поток для обработки обмена данными
voidbwDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Int64 timeoutms = 0;
Int64 timestamplast = DateTime.Now.Ticks;
queryStopWork = false;
int state = CNST.SENDPACKET;
// Начальная настройка обмена:
RxReset();
worker.ReportProgress(state);
while (!queryStopWork)
{
switch (state)
{
case CNST.SENDPACKET:
//Отправка каких-то данных:
Send();
//Таймаут отслеживается относительно начала//отправки команды. В этом примере отслеживается//таймаут в 1 секунду:
timeoutms = 1000;
state = CNST.WAITRESPOND;
worker.ReportProgress(state);
break;
case CNST.WAITRESPOND:
//Ожидание ответа на отправленный пакет:if (timeout > 0)
{
//Таймаут приема не прошел, продолжаем прием:string rx = Received();
//Тут обработка приянятых даных:if (0 != rx.Length)
{
...
if (rx.Length == PACKET_SIZE)
{
state = CNST.RXDONE;
}
worker.ReportProgress(state);
}
}
else
{
//Таймаут приема, ошибка:
state = CNST.ERRTIMEOUT;
worker.ReportProgress(state);
}
break;
case CNST.ERRTIMEOUT:
case CNST.RXDONE:
queryStopWork = true;
break;
}
Thread.Sleep(20);
//Декремент таймаута, если он установлен:
Int64 timestampcurr = DateTime.Now.Ticks;
if (timeout > 0)
{
timeout -= (timestampcurr/10000 - timestamplast/10000);
}
timestamplast = timestampcurr;
}
}
voidbwProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Тут код, работающий с визуальными компонентами формы приложения.
...
}
1. Во-первых, для получения возможности обращения к произвольному полю класса формы (ListBox, Button, любой другой элемент графического интерфейса или любые другие поля класса формы) оно должно быть объявлено с ключевым словом public (общий класс доступа). Имейте в виду, что по умолчанию все бросаемые в форму элементы управления получают класс доступа private (ограниченный класс доступа).
Посмотреть определение элементов графического интерфейса можно в файле имя_класса_формы.Designer.cs (например Form1.Designer.cs). Напрямую изменять атрибуты доступа непосредственно в этом файле не рекомендуется, вместо этого выберите свойства нужного компонента интерфейса, и измените визуальным редактором его свойство Modifiers на значение Public (доступны варианты Public, Protected, Protected Internal, Internal, Private).
2. Во-вторых, добавьте в дочерний класс формы поле, которое будет содержать ссылку на родительскую форму. Инициализируйте это поле значением this сразу после создания экземпляра дочернего класса. Назовите поле понятным образом, что-то наподобие ParentForm. Пример, как это делается, см. в [9].
3. Обращайтесь к данным формы из дочернего класса через поле ParentForm.
4. Для обращения к данным формы из стороннего потока (не относящегося к GUI формы) требуется предпринимать специальные меры по обеспечению безопасности доступа к данным со стороны разных потоков. Подробнее см. [9].
Для этого достаточно использовать обычное приведение типа, т. е. добавить к байту префикс (char). При преобразовании будет использоваться кодировка UTF8. Пример преобразования массива байт bytearr в строку символов str:
//pProcess.StartInfo.CreateNoWindow = true; //позволяет не отображать окно
pProcess.Start();
Пример запуска браузера Chrome для открытия файла HTML:
usingSystem.Diagnostics; // для класса Process
...
Process процесс = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "chrome.exe";
startInfo.Arguments = "Отчет.html";
процесс.StartInfo = startInfo;
процесс.Start();
См. также How do I start a process from C#? site:stackoverflow.com.
Метод RunWorkerAsync для запуска BackgroundWorker может быть вызван без параметров, тогда в тело потока DoWork никакие параметры не будут переданы. Но у метода RunWorkerAsync также есть перезагрузка, которая может принять в качестве аргумента object (т. е. переменную любого типа). Таким образом можно передать в поток любую переменную: int, string или даже пользовательский тип.
Пример запуска потока с передачей в него строки:
//Создание экземпляра потока и настройка его параметров:
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bwDoWork);
//Необязательные обработчики прогресса и завершения потока (их можно не добавлять):
bw.ProgressChanged += new ProgressChangedEventHandler(bwProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwRunWorkerCompleted);
...
Такая проблема обычно возникает в момент закрытия основной формы, т. е. в момент выхода из программы по желанию пользователя (когда он к примеру нажал Alt+F4). В этот момент во избежания разных ошибок нужно корректно завершить все потоки, или дождаться завершения одного их цикла. Конечно, это можно осуществить с помощью установки и проверки глобальных флагов, но однако, как это ни странно, проще всего воспользоваться свойством WorkerSupportsCancellation и методом CancelAsync(), которые доступны у класса потока BackgroundWorker.
Свойство WorkerSupportsCancellation по умолчанию находится в значении false, т. е. поток не поддерживает асинхронное завершение с помощью вызова CancelAsync(). Чтобы можно было в любой момент завершить поток вызовом CancelAsync(), установите в true свойство WorkerSupportsCancellation. Это можно сделать либо в визуальном редакторе (если Вы создали объект BackgroundWorker перетаскиванием его на форму), либо в тексте программы (если Вы создаете объект BackgroundWorker динамически, во время работы программы). Ниже приведен пример создания динамического создания потока (для упрощения код обработчиков не приводится).
BackgroundWorker bw = new BackgroundWorker();
//Поток будет поддерживать асинхронное завершение:
bw.WorkerSupportsCancellation = true;
//Поток будет поддерживать взаимодействие с GUI программы:
bw.WorkerReportsProgress = true;
//Добавление тела потока:
bw.DoWork += new DoWorkEventHandler(bwDoWork);
//Добавление обработчика события прогресса:
bw.ProgressChanged += new ProgressChangedEventHandler(bwProgressChanged);
//Добавление обработчика события завершения потока:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwRunWorkerCompleted);
Теперь в момент закрытия основной формы можно просто вызвать метод CancelAsync(), и поток завершится автоматически, не нужно дожидаться его завершения:
В этом примере медленная функция abd() выполняет некую фоновую работу в потоке, и есть необходимость быстрого завершения её работы. Предполагалось, что это было бы можно вызовом bw.CancelAsync(), но на самом деле ничего подобного не происходит. Почему?
Когда Вы вызвали bw.CancelAsync(), то этим просто установили в true флаг bw.CancellationPending. По умолчанию это ничего не завершает. Вам нужно обработать завершение вручную. Но Вы не можете просто так сделать это в своем коде, потому что при клике на кнопку в интерфейсе GUI, когда нужно завершить поток, возможны 3 случая:
• Работает какая-то медленная функция внутри цикла потока, и по этой причине завершение не произойдет. • Медленная функция начала свою работу, и поток этим заблокировался. Он ждет завершения этой функции, и продолжает свое выполнение. Если в циклах функции будет осуществляться проверка флага CancellationPending с целью ускоренного завершения работы, то таким способом можно быстро завершить работу потока и вызвать тем самым событие RunWorkerCompleted. • Довольно мало вероятный случай - Вы были быстры как метеор, и кликнули на кнопку завершения поток перед входом в блок проверки if-else, перед запуском медленной функции. Тогда флажок CancellationPending установится в true, и медленная функция не начнет свою работу, поток нормально завершится.
Если Вы хотите использовать асинхронное завершение потока, то когда делаете запуск в цикле какого-то медленного вычисления, разбейте эти вычисления на отдельные повторяющиеся итерации цикла, и проверяйте флаг завершения на каждой итерации. Например:
voidbw_DoWork(object sender, DoWorkEventArgs e)
{
List< Foo > results = new List< Foo >();
// В этом месте может быть любой цикл - foreach, whilefor(int i = 0; i < steps_count; i++)
{
// Проверка статуса перед каждой итерацией.if (bw.CancellationPending == true)
{
e.Cancel = true;
return; // Остановка работы, если было запрошено завершение потока
}
results.Add(abd()); // добавление результатов вычислений
}
e.Result = results; // возврат всех результатов
}
Расширение *.pdb произошло от сокращения Program DataBase, т. е. база данных программы. Она создается при компиляции приложения, и используется для отладки и хранения информации о состоянии проекта. Этот файл по умолчанию создается как для конфигурации Debug, так и для конфигурации Release. Для конфигурации Release размер этого файла обычно немного меньше. Если Вы не хотите создавать этот файл (например, когда для конфигурации Release он не нужен), откройте свойства проекта, перейдите в раздел настроек Построение, кликните на кнопку Дополнительно... (Build -> Advanced...), и в выпадающем списке "Отладочная информация:" (Debug Info:) выберите none.
Из-за установок библиотек различных версий может появиться предупреждение примерно такого вида (отображается в окне результатов компиляции "Вывод"):
ResolveAssemblyReferences:
Будет создан список исключений профиля TargetFramework.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(1605,5): warning MSB3247:
Обнаружены конфликты между различными версиями одной и той же зависимой сборки.
Для устранения конфликта нужно получить больше информации о причине, для этого поменяйте уровень выдачи сообщений сборки проекта на "Подробный" (Detailed).
2. В левой части окна находится дерево параметров, раскройте узел Projects and Solutions (Проекты и решения), после чего выберите раздел настроек Build and Run (Построение и запуск).
3. Установите в выпадающем списке MSBuild project build output verbosity (Степень подробности сообщений при построении проекта MSBuild) в значение "Подробные" (по умолчанию там задано "Обычные"). Это делается по-разному в зависимости от версии Visual Studio:
Для VS2012, VS2013 или VS2015 установите "Diagnostics". Для VS2010 "Detailed" ("Подробные"). Для VS2008 или более старых версий Normal.
4. Пересоберите проект, чтобы увидеть подробный лог сборки.
Проанализируйте сообщений MSBuild. Сообщение ResolveAssemblyReferences, которое относится к генерации предупреждения MSB3247, должно помочь Вам понять причину проблемы.
В моем конкретном случае причина была в SDK старой версии, который был установлен после Visual Studio. Переустановкой Visual Studio проблема была решена.
Для диагностики можно также использовать следующее:
• Просмотрите свойства (Properties) каждой ссылки на библиотеку. Все ссылки, используемые в проекте, находятся в папке Ссылки (References). • Откройте свойства проекта и проверьте версии в секции Ссылки (References). • Откройте файл проекта с помощью текстового редактора. • Используйте .Net Reflector. • Консольная утилита с открытым исходным кодом AsmSpy (а также AsmSpyPlus с графическим интерфейсом, доступная на GitHub) поможет разобраться с конфликтом версий сборок.
error MSB3323: Не удалось найти сертификат подписи манифеста в хранилище сертификатов.
Ошибка обычно возникает, когда Вы переименовали проект, подписаный сертификатом, или когда компилируете чужой, подписанный сертификатом проект. Как исправить: откройте свойства проекта, перейдите в раздел "Подписывание" (Signing) и снимите галочку с опции "Подписать манифесты ClickOnce" (Sign the ClickOnce manifests).
После установки Visual Studio 2019 Community на Windows 7 x64 система потребовала перезагрузки (она в процессе установки тихой сапой поставила обновления, никого не спросясь). Перезагрузился, при старте системы долго висел черный графический экран, после чего система позволила сделать логин. И оказалось, что все программы перестали запускаться с выдачей ошибки 0xC0000005. Повторная перезагрузка не помогла.
Гугление показало, что не я первый сталкиваюсь с этой проблемой. Помогла ссылка [11]. Суть в том, что нужно удалить обновления, запустив следующие команды в консоли с правами администратора:
Чтобы этого не происходило, необходимо вставить в тело цикла (который находится в DoWork) вызовы Sleep, чтобы дать время потоку формы, который обрабатывает работу визуальных компонентов GUI:
System.Threading.Thread.Sleep(1);
В качестве параметра Sleep принимает длительность в миллисекундах.
Результат вычитания между двумя значениями DateTime автоматически получает тип TimeSpan. Для преобразования TimeSpan в строку используют его метод ToString со строкой формата. Пример:
Для отображения часов в 12-часовом формате надо указывать для часов формат hh, а для отображения в 24-часовом формате формат HH.
String ДекодироватьВремя(DateTime время)
{
// Отображение времени в 12-часовом формате://return время.ToString("hh:mm:ss");// Отображение времени в 23-часовом формате:return время.ToString("HH:mm:ss");
}
Пример заполнения в коде загрузки формы Form1_Load списка меню именами COM-портов в системе, с привязкой к элементу (пункту) меню обработчика клика МенюПортКлик:
1. Сначала надо переопределить конструктор отображаемой второй формы, чтобы он принимал два параметра - верхнюю и нижнюю позиции формы (T и L). Также надо определить две переменные, где будут храниться эти позиции (vTop и vLeft).
2. В обработчик события Shown формы надо добавить код, модифицирующий поля Left и Top формы новыми координатами, которые были переданы в конструкторе.
privatevoid OnChanged(object source, FileSystemEventArgs e)
{
// Здесь по аргументу e можно определить, что произошло:// был ли файл изменен, создан или удален.
listBox1.Items.Add(String.Format("File: {0} {1}",
e.FullPath, e.ChangeType));
}
Проблему можно решить, если применить делегата и проверку InvokeRequired на том элементе управления, к которому нужен доступ из другого потока (в этом примере listBox1):
Попытка завершить поток потерпела неудачу - поток не находится в теле цикла DoWork, вызов метода CancelAsync, даже ожидание завершения потока не помогает - IsBusy всегда установлено в true.
...
if (bw.IsBusy)
{
bw.CancelAsync();
while (bw.IsBusy)
Thread.Sleep(10); // бесконечное зацикливание...
}
В чем причина, как исправить проблему?
Для завершения потока BackGroundWorker должно отработать событие RunWorkerCompleted оно не запустится, если не дать возможность приложению обработать события. Для этого в цикле ожидания надо вызвать Application.DoEvents():
...
if (bw.IsBusy)
{
bw.CancelAsync();
while (bw.IsBusy)
Application.DoEvents():
}
В классе TabControl закладки представлены коллекцией экземпляров класса TabPage. У класса TabPage свойство Visible появилось, начиная с библиотеки .NET версии 5.0. Для более старых версий, чтобы скрыть закладку TabPage, можно использовать следующий трюк (myTab это экземпляр класса TabPage, а myTabCtrl это экзмпляр его родительского класса TabControl):
Как поменять картинку в pictureBox при наведении на него курсора мыши, а при убирании курсора появляется пред идущая картинка. И прошу сказать где необходимо (в какой папке Проекта) разместить обе картинки?
microsin: воспользуйтесь методами get и set свойства PictureBox.Image. Чтобы определить момент смены картинки, обрабатывайте события MouseEnter и MouseLeave объекта PictureBox. Где у Вас расположены картинки не имеет значения, главное правильно добавить на них ссылки.
При запуске программы появляется два окна: одно с путём к файлу проекта (находится над вторым окном), второе окно имеет путь C:WindowsSystem32.cmd.exe. Первое окно висит около 14 секунд (в нём ничего не происходит и закрыть его нельзя), потом закрывается и во втором окне (с путём C:WindowsSystem32.cmd.exe) появляются результаты работы программы. Почему появляется два окна? Как сделать, что было только одно окно?
[quote name="Александр"]При запуске Visual Studio 2010 появляется ошибка: инициализатор типа выдал исключение, и Visual Studio не запускается, в чем проблема?
В какой-то из форм проекта используется компонент, который не отрабатывает правильно инициализацию в режиме дизайна. Возможно так же, что какой-то из файлов поврежден.
Лечение - удалить или переименовать форму, загрузить Visual Studio и пересобрать форму из корректных элементов.
На форме есть Panel. Как передать процедуре Panel1_Paint пару переменных? Глобальные переменные, определенные в public partial class Form1 : Form { почему-то в процедуре Panel1_Paint всегда равны 0.
Как вывести на форму значение переменной типа int?
microsin: сначала нужно перевести число в строку. Сделать это можно с помощью метода Convert (см. Q022), или с помощью преобразований Format. Затем нужно нарисовать полученный текст на форме. Можно это сделать с помощью метки (Label), брошенной на форму, а можно с помощью метода DrawString (см. Q034). Вариантов много.
Комментарии
microsin: воспользуйтесь методами get и set свойства PictureBox.Image. Чтобы определить момент смены картинки, обрабатывайте события MouseEnter и MouseLeave объекта PictureBox. Где у Вас расположены картинки не имеет значения, главное правильно добавить на них ссылки.
microsin: воспользуйтесь обработчиком таймера Timer или потоком BackgroundWorke r, и отображайте оттуда нужные картинки.
В какой-то из форм проекта используется компонент, который не отрабатывает правильно инициализацию в режиме дизайна.
Возможно так же, что какой-то из файлов поврежден.
Лечение - удалить или переименовать форму, загрузить Visual Studio и пересобрать форму из корректных элементов.
Как передать процедуре Panel1_Paint пару переменных? Глобальные переменные, определенные в
public partial class Form1 : Form {
почему-то в процедуре Panel1_Paint всегда равны 0.
microsin: думаю, что даже Microsoft не знает, в чем проблема. Просто переустановите Visual Studio, и ошибка исчезнет.
microsin: сначала нужно перевести число в строку. Сделать это можно с помощью метода Convert (см. Q022), или с помощью преобразований Format. Затем нужно нарисовать полученный текст на форме. Можно это сделать с помощью метки (Label), брошенной на форму, а можно с помощью метода DrawString (см. Q034). Вариантов много.
RSS лента комментариев этой записи