Страницы

Поиск по вопросам

Показаны сообщения с ярлыком winforms. Показать все сообщения
Показаны сообщения с ярлыком winforms. Показать все сообщения

среда, 15 апреля 2020 г.

Как сделать дополнительные кнопки на окне?

#c_sharp #winforms

                    
Как сделать дополнительные кнопки на окне, помимо 3 основных (свернуть, развернуть,
закрыть) на C#?    


Ответы

Ответ 1



Для начала, судя по всему, без использования Win32 не обойтись. (Или третьесторонней библиотеки, к-ая за вас использует Win32). Вот здесь я нашел более менее ответ. Пример оттуда же. (попробовал, правда работает) Далее перевожу вариант, совместимый с Аэро (как наиболее актуальный): Это решение основано на Win32-примере по этой ссылке: http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx В принципе, нужно сделать следующее: Расширить клиентскую область окна так, чтобы она покрывала весь Frame. Для этого нужно перехватить WM_NCCALCSIZE-сообщение и вернуть 0. Так не-клиентская область станет нулевого размера, что значит, что клиентская будет покрывать все окно. Расширить Frame до размеров клиентской области, используя DwmExtendFrameIntoClientArea. Это скажет ОС рисовать Frame над клиентской областью. Вышеописанные шаги дают вам окна со стандартным стеклянным оформлением, исключая иконку окна и заголовок. Кнопки свернуть, развернуть и закрыть будут продолжать рисоваться и работать. Что не будет работать, так это перетаскивание и изменение размеров окна, потому что фрейма теперь нет, а клиентская область покрывает все окно. Теперь можно рисовать на окне как обычно. Можно даже поместить контролы в заголовок. Осталось только перехватывать WM_NCHITTEST-сообщения, брать из них информацию о положении курсора мыши и возвращать информацию о том, в какой части окна он находится. Например, если вернуть HT_CAPTION, то окно будет работать так, как будто курсор находится на заголовке, и будет позволять перетаскивать окно и т.д. С этой функцией получится сделать полнофункциональное окно с кастомным фреймом.

понедельник, 13 апреля 2020 г.

Google Chrome в приложении Win forms

#c_sharp #google_chrome #winforms

                    
Пишу приложение на С#, в котором хотелось бы увидеть браузер crome для использования
google speech. Использовал awesomium, которым открыл страницу но google speech он не
поддерживает. Т.е. Нужен именно хром в моем приложении. Может кто сталкивался с подобным.
Заранее спасибо    


Ответы

Ответ 1



How to develop Desktop Apps using HTML/CSS/JavaScript? Перевод принятого ответа: Можно начать с Titanium'а для десктопной разработки. Также стоит обратить внимание на chromiumembedded. Это по сути контрол веб-браузера, основанного на хромиуме. Он написан на C++ и позволяет использовать все низкоуровневые возможности ОС (Growl, иконки в трее, доступ к локальным файлам, com-порты и т. д.) и при этом писать всю логику и интерфейс приложения на html и javascript. Он также позволяет перехватывать любые http-запросы, чтобы обратится к локальным ресурсам или выполнить особые действия. Например, запрос на http://localapp.com/SetTrayIconState?state=active может быть перехвачен контейнером, который вызовет код на Си++ чтобы обновить иконку в трее. Он позволяет создавать функции, которые могут непосредственно вызываться из js. Но в CEF очень сложно отлаживать js. Там нет ничего похожего на Firebug. Ещё можно посетить AppJS.com (Помогает создавать десктопные приложения под Linux, Windows и Mac используя HTML, CSS и JavaScript). Как дополнил @Clint, команда Bracket.io (Adobe) используя Chromium Embedded Framework создала замечательную оболочку. Она называется brackets shell. Также доступно её описание.

Ответ 2



Возможно, это или вот это будет вам полезно

Ответ 3



Selenium Webdriver под кучу языков идет на ура. Из личного опыта я с ним работаю больше года в Visual Basic 2008

суббота, 11 апреля 2020 г.

Winforms vs winapi

#winapi #winforms #cpp

                    
У меня старая ОС(windows xp) и компилятор(mvs 2010) на данный момент доступны только
две технологии писать свои форточки winforms и голый winapi, какую технологию лучше
выбрать после консольных приложений?    


Ответы

Ответ 1



Если для общего развития и речь именно о развитии в рамках С++, то, пожалуй, я бы предпочел WInAPI - там хотя бы именно С++ используется, в отличие от WInForms, где, насколько я знаю, задействован С++/CLI. Хотя на мой взгляд, и то и другое - не самое лучшее приложение сил. Если же вы планируете связать с какой-то из этих технологий дальнейшую профессиональную деятельность и тем самым зарабатывать, то я бы не советовал связываться ни с тем ни с другим - для создания оконных приложений под Win есть куда более адекватные альтернативы, се-таки на дворе XXI век как-никак.

Windows Forms: как передать параметры между двумя окнами

#c_sharp #winforms

                    
Есть проблемка. У меня есть Главная форма, где происходит весь процесс програмы.
Но я хочу добавить кнопочку "настройки", при нажатии на которую должна открываться
другая форма, там будут текстбоксы, чекбоксы и т.п. Проблема в том, чтобы эти изменения
сохранялись или передавались в главную форму и служили параметрами для используемых
функций. Кто подскажет, как это реализовать попроще?
    


Ответы

Ответ 1



Если очень просто... 1) Сделайте классик, который будет содержать настройки: public class Settings { public string StringSetting { get; set; } public int IntSetting { get; set; } public bool BoolSetting { get; set; } ... } 2) Дальше в форму настроек добавьте свойство с этими настройками, а также два места, где настройки будут считываться и "сохраняться": public class SettingsForm { ... public Settings Settings { get; set; } ... public void OnFormLoad(...) // условное название { textBox.Text = settings.StringSetting; checkBox.Checked = settings.BoolSetting; } public void OnFormClosing(...) // условное название { Settings.StringSetting = textBox.Text, Settings.BoolSetting = checkBox.Checked } } Подсказка: запоминание настроек можно заменить дата байндингом. 3) При вызове формы с настройками устанавливайте ей объект с актуальными настройками: Settings settings = LoadSettings(); using (var frmSettings = new SettingsForm()) { frmSettings.Settings = settings; frmSettings.ShowDialog(); SaveSettings(frmSettings.Settings); } LoadSettings() должен, соответственно, откуда-то считывать настройки, а SaveSettings() -- сохранять. 4) При запуске и выходе из главной формы настройки должны считываться и сохраняться.

четверг, 9 апреля 2020 г.

MonthCalendar реагирует на “пустые ячейки”

#c_sharp #visual_studio #winforms #calendar

                    




В visual studio есть стандартный контрол MonthCalendar.
По умолчанию контрол выглядит как на левой части картинки. 
На нем видно числа следующего месяца и их можно выделить и контрол переместится на
новый месяц.

Если в контроле установить calendar dimension равный 1,2 то контрол примет  вид как
на правой части картинки. Будет видно сразу два месяца. Но если начать выделять, то
происходит нелепица как показано на видео или в гифке. 

Происходит это потому что в пустых "ячейках" календаря есть дни следующего месяца,
но их не видно, т.к. следующий месяц показан полностью из-за calendar dimension. 

Как сделать так чтоб контрол не реагировал на пустых "ячейках", в которых есть данные
о следующем месяце, но их не видно?
    


Ответы

Ответ 1



С этим ничего сделать нельзя. MonthCalendar — это обёртка над системным элементом управления SysMonthCal32. Сам класс никакой особой логики не содержит, поведение реализовано на уровне операционной системы. А вот операционная система настолько глючная, что выделение так странно работает. Или, может быть, кто-то счёл, что это не баг а фича. Так или иначе, это норма. Вероятно, про этот элемент управления забыли давно и основательно. Сейчас модно использовать WPF, UWP и т. п., а WinForms — это легаси.

Почему родитель формы равен Null?

#c_sharp #winforms #null

                    
Выдержка кода из главной формы:

public partial class Main : Form
{
    public NetComm.Host server;
    public NetComm.Client client;
    public Main()
    {
        InitializeComponent();
    }
    private void ToolStripMIConnect_Click(object sender, EventArgs e)
    {
        ClientPref formConnect = new ClientPref();
        formConnect.Owner = this;
        formConnect.ShowDialog();
    }
}


Выдержка кода из создаваемой формы:

public partial class ClientPref : Form
{
    Main formMain;
    public ClientPref()
    {
        InitializeComponent();
        formMain = this.Owner as Main;
    }
    private void btnConnect_Click(object sender, EventArgs e)
    {
        if (formMain != null) //следующий бок не выполняется т.к. formMain равен null
        {
            if (IsAddressValid(this.txtBoxIP.Text))
            {
            formMain.ClientStart(Convert.ToInt32(this.txtBoxPort.Text), this.txtBoxIP.Text,
"Соперник");
            }
            else toolTipClient.Show("Введен некорректный IP-адрес.", this.txtBoxIP);
        }

    }


Почему formMain равен null или как в итоге обратиться к методу ClientStart формы Main?
    


Ответы

Ответ 1



Простой вариант - переписать вот так, сделать поле вычислимым свойством: Main FormMain { get { return this.Owner as Main } }; Тогда в btnConnect_Click всё должно работать.

Ответ 2



Когда выполняется код formMain = this.Owner as Main; owner еще не установлен, так как он устанавливается после выполнения конструктора, поэтому formMain всегда null Решить можно передавая параметр в конструктор, для этого надо добавить конструктор с параметром public ClientPref(Main owner):this() { formMain = this.Owner as Main; } и вызывать уже его private void ToolStripMIConnect_Click(object sender, EventArgs e) { ClientPref formConnect = new ClientPref(this); formConnect.ShowDialog(); }

Ответ 3



Если ClientPref - это диалог, то его надо сделать независимым и он не должен управлять формой, т.е. в диалоге не должно быть вызова formMain.ClientStart(...), т.к. это нарушает один из основных принципов дизайна в ООП - принцип единственной обязанности (англ. Single responsibility principle - SRP). Диалог должен получить данные от пользователя, проверить их и предоставить доступ к этим данных. Больше никакой обязанности у диалога быть не должно. Посмотрите, например, на OpenFileDialog, в нем нет ссылки на главную форму. Чтобы диалог ClientPref сделать независимым, надо значения, выбранные пользователем, сохранять в свойствах диалога. public partial class ClientPref : Form { public int Port { get; private set; } public string IP { get; private set; } void Validate() { if (IsAddressValid(this.txtBoxIP.Text)) { this.Post = Convert.ToInt32(this.txtBoxPort.Text); this.IP = this.txtBoxIP.Text; } } } В диалоге надо перехватывать нажатие Esc -- работающий пример диалога тут. Для вызова диалога и получения данных из него, в главной форме пишем: var fc = new ClientPref(); if(fc.ShowDialog() == DialogResult.OK) ClientStart(fc.Port, fc.IP); т.е. форма решает когда вызвать диалог и что делать с данными введенными пользователем. Если по какой-то причине надо получить ссылки на все открытые формы, то это можно сделать так: using System.Windows.Forms; var forms = Application.OpenForms; Открытую форму определенного типа можно получить так using System.Linq; var main = Application.OpenForms.OfType
().First(); Если есть несколько форм одинакового типа, то их можно отпичать по значению свойства Name, но таких ситуаций лучше избегать. Почему нежелательно передавать ссылки? Представьте, что в приложении десяток форм. В каждую форму передается ссылка на главную форму, а также возможно, что передается ссылка на какую-то дополнительную форму. Если в дальнейшем надо будет изменить логику взаимодействия форм, то возникнут сложности с рефакторингом. Поэтому надо стараться уменьшать связанность кода, и если есть возможность передать ссылку через Application.OpenForms, то лучше так и делать.

BindingList и привязка данных

#c_sharp #net #winforms #list

                    
У меня есть лист в который я могу добавить\удалить какой-то объект , как сделать
чтобы на это событие сразу отреагировал ListBox? 
    


Ответы

Ответ 1



Надо привязать ListBox к коллекции // #r "System.Windows.Forms" using System.Windows.Forms; using System.ComponentModel; var bl = new BindingList() { 1, 2, 3 }; new Timer { Enabled=true, Interval=1000 } // для примера каждую секунду .Tick += (s, e) => bl.AddNew(); // добавляем новые элементы в bl var f = new Form(); var lb = new ListBox() { Parent = f, Dock = DockStyle.Fill, DataSource = bl // привязка ListBox к BindingList }; f.ShowDialog(); Если надо в ListBox выводить значение свойства, то в lb надо добавить ValueMember var lb = new ListBox() { DataSource = bl, ValueMember = "тут имя свойства"; }; Если в ListBox надо выводить изменения свойств объектов, то в объектах надо реализовать интерфейс INotifyPropertyChanged public class Data : INotifyPropertyChanged { // требуется для уведомления о изменении значения свойств. public event PropertyChangedEventHandler PropertyChanged = delegate {}; public int Id { get; internal set; } public long Value { get { return _Value; } internal set { if (_Value != value) { _Value = value; PropertyChanged(this, new PropertyChangedEventArgs("Value")); }}} long _Value = 0; } Работающий пример на c# тут. UPDATE в лист добавляю число 1, а в лист бокс должна вывестись не единица, а "Число 1" Проще всего сделать так class Data { public int Value { get; set; } public string Text { get { return "Число " + this.Value; } } } var bl = new BindingList() { new Data { Value = 1 } }; var lb = new ListBox() { Parent = f, DataSource = bl, ValueMember="Value", DisplayMember="Text" };

Ответ 2



Объявляем класс с данными: public class Person { public int Id { get; set; } public string Name { get; set; } } Создаем коллекцию объектов: var source = new BindingList() { new Person() { Id = 1, Name = "Ivan" } }; Настраиваем контрол и передаем ему данные: // свойство, которое будет отображаться listBox.DisplayMember = "Name"; // свойство, которое будет возвращаться в качестве значения выбранного айтема listBox.ValueMember = "Id"; listBox = source; После этого все изменения в source будут отображаться в контроле.

Ответ 3



Вам нужно воспользоваться ListControl.DataSource List someList = ...; listBox.DataSource = someList;

вторник, 7 апреля 2020 г.

Реализация приложения с обновляемыми формами

#c_sharp #winforms

                    
Стоит задача изменения используемых в приложении форм во время его работы. копал
в сторону выгрузки динамических библиотек (чтобы они содержали формы). Т.е. загрузил
в AppDomain библиотеку, получил нужный 

контрол (допустим, что обновлять нужно не только формы) разместил его где нужно,
отобразил с его помощью нужные данные.

Получив сигнал о необходимости обновить контрол - свернули всё это хозяйство, выгрузили
домен, получили новую версию DLL, создали новый домен - и поехали заново.

Проблемы, возникшие по ходу реализации:


Вся инфа по работе между доменами ведет к интерфейсам и неким абстрактным классам
- а мне нужен хотя бы System.Forms.Control.
При обмене данными между доменами приложений используется маршаллинг. Используемые
контролы имеют всего лишь несколько внешних свойств, которые позволяют их настроить
и для получения данных они используют 


OracleConnection, который не может быть передан между доменами (он не маршаллится). 

т.е. выходит, что подход изначально не верный - загрузка библиотеки в другой домен,
создание контрола отображения для данных из oracle-сессии из первого домена.

Как можно реализовать набор таких обновлямых контролов, причем не прерывая соединения
с бд, т.е. не выгружая ехе, если это реально?

UPDATE
попробовал передать между доменами

общая библиотека OracleCall.dll
    //интерфейс для класса, через который передаю оригинальный оракловый коннекшн
    public interface IConnectInitializer
    {
        void CreateConnectionClone(Oracle.DataAccess.Client.OracleConnection conn);
    }

вторая библиотека libForNewClasses.dll, содержащая ссылку на общую 
в этой библиотеке реализуется статический хранитель оралового соединения:

public class oracleConnInitializer : MarshalByRefObject, IConnectInitializer
{
    public static Oracle.DataAccess.Client.OracleConnection conn;

    public void CreateConnectionClone(Oracle.DataAccess.Client.OracleConnection connection)
    {
        conn = (Oracle.DataAccess.Client.OracleConnection)connection.Clone();
        conn.Open();
    }
}


и контрол:

public class newControl : UserControl
{
    private void newControl_Load(object sender, EventArgs e)
    {
        OracleCall.OraCallProc.getData(oracleConnInitializer.conn);
    }
}


третий модуль, ехе-шник, содержит ссылку на первый, общий, чтобы работать с передачей
OracleConnection
в нем находится форма, на которую и нужно положить контрол из второй библиотеки.

public partial class Form1 : Form
{

// образец соединения
    static Oracle.DataAccess.Client.OracleConnection conn = new Oracle.DataAccess.Client.OracleConnection(connString);

// домен, куда будем грузить третью библиотеку с дополнительным контролом
    AppDomain dmn;


    public Form1()
    {
        InitializeComponent();
        init();
    }
    void init()
    {
        // создаем домен
        dmn = AppDomain.CreateDomain("newDomain");

        // загружаем библиотеку с контролом
        Assembly asm = dmn.Load("libForNewClasses");

        // создаем передатчик коннекшена
        IConnectInitializer connInit =
            (IConnectInitializer)
            dmn.CreateInstanceAndUnwrap("libForNewClasses", "libForNewClasses.oracleConnInitializer");

        // передаем коннекшн
        connInit.CreateConnectionClone(conn);

        // создаем контрол
        Control ctrl =
            (Control)
            dmn.CreateInstanceAndUnwrap("libForNewClasses", "libForNewClasses.newControl");

        this.Controls.Add(ctrl);// пытаемся добавить - БАБАХ! ОШИБКА!!!!
    }
}


ОШИБКА: Remoting cannot find field 'parent' on type 'System.Windows.Forms.Control'.

Что я делаю не так?

UPDATE 2
переделал общую библиотеку. дабы придать ей общий вид обзовем ее common.dll
теперь она содержит образец контрола:
    public class myControls : UserControl
    {
        private Button button1;

    private void InitializeComponent()
    {
        // ...
    }
    public IntPtr _handle { get; set; }
    public void SetParentHandle(IntPtr handle) { _handle = handle; }
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.Parent = _handle; // Сюда надо передать Handle формы
            return createParams;
        }
    }
    protected Oracle.DataAccess.Client.OracleConnection Connection;
    public void InitConnection(Oracle.DataAccess.Client.OracleConnection conn) {
Connection = conn; }
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        InitializeComponent(); // почему-то без этого не видно
    }
}


дочерний контрол также изменил свой код:

public class newControl : myControls
{
    private Label label1;

    private void InitializeComponent()
    {
        // ...
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        InitializeComponent(); // почему-то без этого не видно
        OraCallProc.getData(Connection); // БАБАХ! падаем с ошибкой!     
    }        
}


ушел от статического хранения соединения, теперь на главной форме:

public partial class Form1 : Form
{
    // образец соединения
    static Oracle.DataAccess.Client.OracleConnection conn = new Oracle.DataAccess.Client.OracleConnection(connString);
    // домен, куда будем грузить третью библиотеку с дополнительным контролом
    AppDomain dmn;
    public Form1()
    {
        InitializeComponent();
        init();
    }
    void init()
    {
        // открываем
        conn.Open();
        // создаем
        dmn = AppDomain.CreateDomain("newDomain");
        // грузим сборку в новый домен
        Assembly asm = dmn.Load("libForNewClasses");
        // создаем контрол
        myControls ctrl =
            (myControls)
            dmn.CreateInstanceAndUnwrap("libForNewClasses", "libForNewClasses.newControl");
        // отправляем контролу соединение
        ctrl.InitConnection(conn);
        // устанавливанем хэндл формы
        ctrl.SetParentHandle(this.Handle);
        ctrl.Visible = true;
        ctrl.CreateControl(); // БАБАХ! падаем с ошибкой! (это та, что на OnLoad)
    }
}


вопросы:

1) ошибка: Remoting cannot find field 'm_collRef' on type 'Oracle.DataAccess.Client.OracleParameter'.

2) даже если не подключаться к базе, newControl теряет весь свой вид. Почему приходится
самому переинициализировать в OnLoad? Не красиво ведь в каждом наследнике переопределять

3) т.е. для каждого типа нового контрола нужно создать наследника (будь то TextEdit,
ListView и подобных), даже если нет необходимости подключаться к БД, мне нужен метод
SetParentHandle? Как-то это негибко и громоздко получается
    


Ответы

Ответ 1



Внимание, ответ по ''Remoting cannot find field 'm_collRef' on type'' Ошибка была не в передаче соединения. С ним работа была налажена. Она крылась в процедуре OraCallProc.getData(Connection); это был статический метод, который выглядел примерно так: public static DataTable getData(OracleConnection conn) { using (OracleCommand cmd = conn.CreateCommand()) { cmd.CommandType = System.Data.CommandType.StoredProcedure; cmd.CommandText = "TESTSHEME.TESTPROC.GETDATAFUNCTION"; OracleParameter outvalue = new OracleParameter(); // неправильно! объект создается не в том домене!!! outvalue.OracleDbType = OracleDbType.RefCursor; outvalue.Direction = ParameterDirection.ReturnValue; outvalue.ParameterName = "OUTP"; cmd.Parameters.Add(outvalue); // при попытке добавить объект из одного домена к списку параметров команды из другого возникает ошибка с m_colRef // вообще и здесь не верно. OracleDataAdapter также создается в другом домене OracleDataAdapter da = new OracleDataAdapter(cmd); DataTable dt = new DataTable(); da.Fill(dt); // здесь адаптер пытается обратиться к соединению из другого домена, пытается изменить m_state, и тоже падаем return dt; } } решить проблему получилось таким образом: public static DataTable getData(OracleConnection conn) { using (OracleCommand cmd = conn.CreateCommand()) { cmd.CommandText = "TESTSHEME.TESTPROC.GETDATAFUNCTION"; cmd.CommandType = System.Data.CommandType.StoredProcedure; OracleParameter outvalue = cmd.CreateParameter(); //создается в том же потоке, что и cmd outvalue.OracleDbType = OracleDbType.RefCursor; outvalue.Direction = ParameterDirection.ReturnValue; outvalue.ParameterName = "OUTP"; cmd.Parameters.Add(outvalue); // поэтому здесь нет ошибки междоменного доступа к внутренним полям // OracleDataAdapter не нашел как создать в том же домене, зато есть ExecuteReader DataTable dt = new DataTable(OracleDataAdapter.DefaultSourceTableName); using (OracleDataReader r = cmd.ExecuteReader()) { for (int i = 0; i < r.FieldCount; i++) dt.Columns.Add(r.GetName(i), r.GetFieldType(i)); object[] rowValues = new object[r.FieldCount]; while (r.Read()) { // не нашел метода получения значений всей строки, получаю поштучно for (int i = 0; i < r.FieldCount; i++) // мне нужны "чистые" значения, поэтому DBNull -> null rowValues[i] = object.Equals(r.GetValue(i), DBNull.Value) ? null : r.GetValue(i); dt.Rows.Add(rowValues); } } return dt; } } Так что междоменное взаимодействие с OracleConnection вполне реально, надо быть просто внимательнее. Всем спасибо за помощь

Ответ 2



Попробуйте в дочернем контроле перегрузить свойство CreateParams, куда записать дескриптор родительского контрола: protected override CreateParams CreateParams { get { CreateParams createParams = base.CreateParams; createParams.Parent = ...; // Сюда надо передать Handle формы return createParams; } } При этом добавлять такой контрол в коллекцию Controls не нужно. Вместо этого надо передать ему свой Handle, установить Visible = true и вызвать метод CreateControl().

суббота, 4 апреля 2020 г.

Вставка текста в текст combobox , который не является его элементом

#c_sharp #winforms

                    
У меня есть combobox с элементами. Дело в том что при выборе элемента я хочу в текст
combobox-a вставить некий другой текст. ну например combobox.Text="zzz". Почему-то
не получается. В чем причина, не смогли бы объяснить. 

Самое интересное в breakpoint-e в свойстве Text="zzz", но вот в форме ничего не отображается.
    


Ответы

Ответ 1



private void cboxScet_SelectionChangeCommitted(object sender, EventArgs e) { this.BeginInvoke((MethodInvoker)delegate { cboxScet.Text = "Нужный текст"; }); }

четверг, 2 апреля 2020 г.

Как в Windows Forms сделать надчеркивание?(логическое отрицание)

#c_sharp #winforms

                    
Именно на формах, а не на wpf. 
Допустим, по нажатию кнопки выделенный текст надчеркивается, например: 

    


Ответы

Ответ 1



В WinForms с красивостями плохо. Надчеркивание стандартными средствами не возможно в принципе, т.к. за стиль шрифта в System.Drawing (GDI+) отвечает перечисление FontStyle, которое ни чего не знает о надчеркивании. RichTextBox тут тоже не помощник, потому что его контент рисует все тот же System.Drawing (GDI+). В этом легко убедиться на примере стандартного WordPad, в котором раскрыты все или почти все возможности RichTextBox в плане форматирования текста, не хватает только некоторых возможностей rtf-разметки, которые впрочем не особенно и нужны в виду наличия более удобных и современных форматов. Альтернативные варианты: Для одной буквы можно воспользоваться комбинированными символами unicode, а именно символом надчеркивания: код 0x0305, например так: "a\x0305" - a̅; или так:"Y\x0305" - Y̅. Для длинного текста такой вариант не подходит, т.к. потребуется вставлять этот символ после каждой буквы текста. Плюс ко всему, такие составные символы не всегда корректно отображаются, надстрочные и подстрочные знаки могут "съезжать" в сторону, например так: ы̅ Можно разместить на форме хостинг для WPF контролов ElementHost и воспользоваться всей оформительской мощью WPF. На самый крайний случай всегда остается возможность просто нарисовать и текст и линии надчеркивания средствами System.Drawing (GDI+) на обычном Bitmap или прямо на контроле, но как вы понимаете, ни каком выделении текста мышкой и редактировании не может быть и речи, если конечно вы не страдаете от острого приступа мазохизма и/или трудоголизма =) ну или интерес исключительно академического характера.

Создание акта в word C#

#c_sharp #winforms #ms_word #office_interop

                    
Доброго времени суток. Нужно создать акт в word на основе данных, имеющихся на winforms.
Подскажите, как лучше это сделать или с чего вообще начать? 
На листе будет отформатированный текст и таблица, число строк в которой каждый раз
будет разным, но не более 10.
Примерный внешний вид акта можно увидеть на фото ниже. Места, где замазано, как раз
надо будет заменить на свои. Ну и, как писал выше, добавить строки в таблицу. В изначальном
шаблоне есть только шапка таблицы, без строк.

    


Ответы

Ответ 1



Используй стандартное расширение Microsoft.Office.Interop.Word и подготовь шаблон с данного документа с закладками в требуемых местах. Краткий код работы с документом: Word._Document document; Word._Application application=new Word.Application(); object missingObj = Missing.Value; object templatePathObj = documentpath; //открытие файла try { document = application.Documents.Add(ref templatePathObj, ref missingObj, ref missingObj, ref missingObj); } catch { document.Close(ref falseobj, ref missingObj, ref missingObj); application.Quit(ref missingObj, ref missingObj, ref missingObj); document = null; application = null; } //заполнение закладок object bookmarkObj = "закладка"; Word.Range bookmarkRange = document.Bookmarks.get_Item(ref bookmarkObj).Range; bookmarkRange.Text = ""; //работа с таблицей Word.Table _table = _document.Tables[tableNumber]; //Выбрать уже существующую таблицу внутри документа можно по ее порядковому номеру (начиная с 1 и начала документа) _table.Rows.Add(ref _missingObj); Word.Range _currentRange = _table.Cell(rowIndex, columnIndex).Range; _currentRange.Text=""; //Вывод на печать document.PrintOut(ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj); document.Close(Word.WdSaveOptions.wdDoNotSaveChanges); application.Quit(); Подробно об этом расширении есть тут и тут.

вторник, 31 марта 2020 г.

Не меняется цвет кнопки если контрол динамический

#c_sharp #winforms


Задача при нажатии на кнопку создать еще одну кнопку с определенным цветом кнопки.
 Добавляем на форму кнопку при нажатии на которую мы создаем кнопку с заданным цветом.

    private static int X = 100;
    private static int Y = 100;
    private void buttonDynamic_Click(object sender, EventArgs e)
    {
        AddButton(X, Y);
        X += 100;
    }
private void AddButton(int x, int y)
    {
        // создаем контрол
        System.Windows.Forms.Button buttonDyn = new System.Windows.Forms.Button(); 
        // устанавливаем необходимые свойства
        buttonDyn.Location = new System.Drawing.Point(x, y);           
        buttonDyn.Name = "button1";
        buttonDyn.Size = new System.Drawing.Size(75, 23);
        buttonDyn.TabIndex = 0;
        //вот здесь меняем цвет кнопки
        buttonDyn.BackColor = Color.Chartreuse;
        buttonDyn.UseVisualStyleBackColor = true;
        // button1_Click - функция обработчик события нажатия на кнопку
        buttonDyn.Click += new System.EventHandler(button1_Click); 
        Controls.Add(buttonDyn); // добавляем на форму

    }


При нажатии на кнопку buttonDynamic появляется новая кнопка, но цвет ее стандартный
серый. Что я делаю не так?
    


Ответы

Ответ 1



Свойство BackColor относится к так называемым AmbientProperties. Смотрите Remarks. Его значение задаётся таким же, как у родительского контрола (это сделано для облегчения конструирования: добавляем контрол - его параметры определяются автоматически). Пока контрол не добавлен на родительский, нет смысла менять такое свойство. Его нужно менять после добавления. Controls.Add(buttonDyn); buttonDyn.BackColor = Color.Chartreuse;

четверг, 19 марта 2020 г.

C# запуск консольного приложения из программы.

#c_sharp #winforms


Всем доброго времени суток. Есть необходимость запустить консольное приложение из
своей программы. Но так, чтобы эта консоль НЕ закрывалась после вызова. А то, только
и видно, что консоль запустилась и сразу же закрылась. Желательно, чтобы консоль не
отображалась. То есть, работала в скрытом режиме.
System.Diagnostics.Process.Start("путь к консольному приложению");

И желательно запускать, а точнее, даже важно, запускать приложение с командой.
В виже: путь-к-программеconsole.exe --conamd

Спасибо за помощь. Всем хорошего настроения. :)    


Ответы

Ответ 1



//создание параметров var startInfo = new ProcessStartInfo { //имя файла FileName = "fileName.exe", //скрытое окно WindowStyle = ProcessWindowStyle.Hidden, //ваши аргументы Arguments = "-command" }; //запуск процесса Process.Start(startInfo);

Перемещаемая форма со скрытыми границами

#c_sharp #winforms


Возможно ли сделать так, чтобы форму можно было перемещать мышкой за ее главное окно,
даже если у нее скрыты границы?
p.s. + еще ко всему этому чтобы она Resizable была так же.    


Ответы

Ответ 1



Конечно такое возможно. Один из вариантов - послать окну сообщение WM_NCLBUTTONDOWN на нажатие мыши, которое сигнализирует о нажатии левой клавиши мыши вне клиентской части окна, то есть на скрытых границах. Однако, перед этим действием необходимо снять "захват" курсора окном, иначе сообщение будет просто проигнорировано. private const int WM_NCLBUTTONDOWN = 0xA1; private const int HTCAPTION = 0x2; protected override void OnMouseDown(MouseEventArgs e) { Cursor = Cursors.Hold; Capture = false; Message msg = Message.Create(Handle, WM_NCLBUTTONDOWN, (IntPtr)HTCAPTION, IntPtr.Zero); DefWndProc(ref msg); Cursor = Cursors.Default; } С ресайзингом придётся повозится дольше, тем более если его делать на этой же самой форме. Как вариант - сначала сделать ресайз только за правый нижний угол. Придётся переопределить оконную процедуру Form.WndProc и самому обработать сообщение WM_NCHITTEST. Кстати, первую проблему можно тоже решить в этом месте. Более подробно смотрите здесь.

среда, 18 марта 2020 г.

Как “выдернуть” значение из ячейки DataGridView?

#c_sharp #datagrid #winforms


Добрый день!Создал Форму1,добавил DataGridView и кинул событие для Формы1:
private void Form1_Load(object sender, EventArgs e)
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_VideoController");
        foreach (ManagementObject queryObj in searcher.Get())
        {
            dataGridView1.Rows.Add(queryObj["Caption"].ToString(), queryObj["PNPDeviceID"].ToString());
        }
    }

Далее для button'a в третьей колонки и кинул событие:
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        string url = "http://сайт.ru" + //тут значение из второй колонки// ;
        Process.Start(url);
        Close();
    }

В итоге вышла таблица,где выходит Имя и Значение ("Caption" и "PNPDeviceID"),а в
третьей колонке по нажатию на button идет переход на сайт.
Вопрос...как выдернуть значение "PNPDeviceID" из второй колонки и подставить в код
где написано "//тут значение из второй колонки//"...т.е. чтобы по нажатию на button
был переход на сайт с учетом значения во второй колонке "PNPDeviceID"?
Надеюсь понятно поставил вопрос.
Заранее спасибо.     


Ответы

Ответ 1



dataGridView1.Rows[e.RowIndex].Cells[1].Value

Ответ 2



Если я правильно понял, в хэндлере кнопки из сендера вытащите номер строки, возьмите значение второй ячейки в этой строке и подставьте куда вам надо.

Ответ 3



CompanyGridView["CompanyID", e.RowIndex].Value; теперь куда бы ты не перемещал столбец, всегда получишь его значение, CompanyID - имя столбца в CompanyGridView

воскресенье, 15 марта 2020 г.

Ассоциация программы с собственным типом файла

#c_sharp #net #winforms #clickonce


Имеется файл программы в виде:

[project]
type=MyApp 1.0 project
directory=D:\Папка проекта
Еще какие-то значения.


При открытии файла внутри программы (Файл->Открыть проект) всё работает замечательно.
Но появилась нужда открывать этот файл двойным щелчком.
С помощью ClickOne создал файл ассоциаций. 

Открытие проекта пытаюсь сделать так:

private void MainForm_Load(object sender, EventArgs e)
{
    string[] args = Environment.GetCommandLineArgs();
    if (args.Length >= 2)
    {
        MessageBox.Show("Открыт проект! УРА!");
        OpenProject(args[1]);
    }
}


В Program.cs прописано, что принимаются аргументы:

public static void Main(string[] args)


Как решить эту проблему?
    


Ответы

Ответ 1



При регистрации с помощью ClickOnce аргументы приходят не в командную строку, а в AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData: foreach (string arg in AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData) MessageBox.Show(arg);

Как правильно делать почти одинаковые формы?

#c_sharp #winforms


Допустим у меня есть приложение с двумя таблицами. Каждая таблица имеет разные поля.
В приложении есть форма для заполнения одной из таблиц. На форме есть такие элементы
как dataGridView, contextMenuStrip. У каждого из этих элементов есть обработчики различных
событий.

Как правильно поступить если нужно сделать вторую форму, с тем же функционалом, но
для работы с другой таблицей? Первое, что приходит в голову - это создать еще одну
форму, разместить те же элементы, написать те же обработчики. В крайнем случае скопировать
и немного поправить код. Но, может есть более правильный подход? Ведь речь может идти
не об одной-двух формах и таблицах, а о десятках.
    


Ответы

Ответ 1



Как вариант, сделать одну форму и при её создании или вызове передавать те или иные параметры параметры для её настройки. Так часто поступают, когда речь идёт о большом количестве однотипных форм.

воскресенье, 8 марта 2020 г.

Вызов метода для неизвестного элемента WinForm

#c_sharp #winforms


public void FunctionA(Object element)
{
 element.Items.Clear();
}


Пример использования:

FunctionA(checkedListBox1);


В функцию FunctionA будут поступать аргументы только checkedListBox и comboBox, у
обоих элементов есть метод .Items.Clear();

Как сделать чтобы это заработало ?
    


Ответы

Ответ 1



public void FunctionA(IList aList) { aList.Clear(); } FunctionA(checkedListBox1.Items); FunctionA(comboBox1.Items); Update Что делать если в случае добавления checkedListBox1, нужно по умолчанию чекбокс в положении cheked. Ну нет чудес. if (aList is CheckedListBox.ObjectCollection) { ((CheckedListBox.ObjectCollection)aList).Add(Text, true); } else { aList.Add(Text); }

Ответ 2



Ну как-то так: if (element.GetType() == typeof(CheckedListBox)) ((CheckedListBox)element).Items.Clear(); else if (element.GetType() == typeof(ComboBox)) ((CheckedListBox)element).Items.Clear();

Работа с radioButton'ом в C# WinForms

#c_sharp #winforms


Пишу программу, где нужно использовать клавиши "влево" и "вправо". Нужно выбирать
правильный ответ и переключать клавишей "влево" или "вправо" для перехода к другому
вопросу. Все работает, только есть одна проблема, когда наживаешь клавишу с клавиатуры,
она сначала переключает значение  radioButton'а, а уже потом переходит к другому вопросу. 

Возможно ли задать какое-то свойство для  radioButton'а, чтобы он не реагировал на
клавиатуру?
    


Ответы

Ответ 1



попробуй у всех радиобаттонов установить кейдаун ивент на этот метод (не нужно его дублировать -- у всех на один и тот же метод) private void radioButton1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { if (e.KeyCode == System.Windows.Forms.Keys.Left || e.KeyCode == System.Windows.Forms.Keys.Right || e.KeyCode == System.Windows.Forms.Keys.Up || e.KeyCode == System.Windows.Forms.Keys.Down) e.SuppressKeyPress = true; } Пока в фокусе элемент подписанный на такой обработчик, клавиши просто игнорируются. Совсем. Никакие другие обработчики не вызываются.

Ответ 2



Такое В WinForms не сделать. По крайней мере, с ходу. Максимум чего можно добиться без "танцев с бубном" - отключение перехода по клавише Tab с помощью свойства TabStop. Но, оно не действует на клавиши "со стрелками". Советую посмотреть в сторону WPF. Там возможностей больше.

Ответ 3



Если у клавиш со стрелками единственное назначение на форме (или контроле-контейнере), то можно перехватить и обработать их на уровне формы, не передавая события от этих клавиш вложенным контролам. Делается это довольно просто, достаточно у формы переопределить метод обработки клавиш: protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { switch (keyData) { case Keys.Left: //код переключения на предыдущий вопрос return true; case Keys.Right: //код переключения на следующий вопрос return true; default: //для всех остальных клавиш оставляем базовую обработку return base.ProcessCmdKey(ref msg, keyData); } } Есть еще одно альтернативное решение с использованием MenuStrip, оно описано в этом ответе. Если в вашей программе используется меню, то этот вариант возможно будет интересен.

Элемент управления типа UpDown

#c_sharp #winforms #usercontrol


В WinForms есть стандартный контрол NumericUpDown:



Хочется получить такой же, но без поля ввода. Т.е. по сути нужны только кнопки вверх/вниз.
Понятно, что можно сляпать свой из двух кнопок и с изображением стрелок, но хотелось
бы унификации со стандартным, чтобы на разных машинах или при смене настроек интерфейса
пользователя мой контрол и стандартный выглядели единообразно.

Какие шаги следует предпринять для достижения нужного эффекта?
    


Ответы

Ответ 1



Попробуйте унаследоваться от предка NumericUpDown: UpDownBase и лимитировать размер контрола размером области кнопок: using System.Windows.Forms; using System.Reflection; using System.Drawing; public class UpDownControl : UpDownBase { private readonly Control upDownButtons; public UpDownControl() { // Достаем internal свойство UpDownButtonsInternal var prop = GetType().GetProperty("UpDownButtonsInternal", BindingFlags.NonPublic | BindingFlags.Instance); if (prop != null) upDownButtons = (Control)prop.GetValue(this, null); } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); int gapWidth = Width - upDownButtons.Right + 1; // Либо const int gapWidth = 2; Size = new Size(upDownButtons.Width + gapWidth, upDownButtons.Height); } public override void DownButton() { } public override void UpButton() { } protected override void UpdateEditText() { } } События нажатий на кнопки будут прилетать в методы DownButton и UpButton соответственно.