Совершенный код. 02. Классы.

Класс - это набор данных и методов, имеющих общую, целостную, хорошо определенную сферу ответственности. Данные - необязательный элемент, в классе могут быть только методы. Основной целью создания классов является разделение программы на как можно более сильно изолированные друг от друга части, чтобы при работе над одной из них можно было не думать о других частях программы. Качественные классы можно назвать абстрактными типами данных, без понимания этой концепции программисты создают классы, в которых от изначальной идеи остается только название - просто контейнеры с набором плохо связанных между собой данных и методов. Суть в том, что сложные объекты рассматриваются как новый тип данных. К числам и строкам добавляются графические окна, таблицы, шрифты, люди, файлы. Абстрактные типы данных позволяют скрыть реальную сложность программы и сделать код более понятным и простым в поддержке.

Разумные причины для создания классов:

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

- Изоляция сложности. Любые ошибки гораздо проще найти и исправить, если они скрыты внутри одного класса, а не разбросаны по всей программе.

- Ограничение влияния изменений. Гораздо проще изменить один класс, нежели исправлять разные части программы.
Читать далее

LinQ, значение по умолчанию в выборке

Задача: в LinQ запросе вернуть единственное значение или значение по умолчанию если запрос не вернет ни одного элемента.

В принципе есть стандартные методы. SingleOrDefault возвращает значение по умолчанию и выбрасывает исключение если запрос вернул несколько элементов, FirstOrDefault не выбрасывает исключения, если запрос вернул несколько параметров и просто выдает первое значение. Но у них есть один недостаток - невозможно задать значение по умолчанию, которые они вернут в случае отсутствия результатов. Более того, возвращаемые ими значения невнятные и недокументированные, в основном null.

А если мы хотим например для строки сразу вернуть пустую строку или "не найдено"? В данном случае нам поможет метод DefaultIfEmpty, данный оператор добавит дополнительный фильтр на запрос, после чего можно вызвать простейший Single

string someString = _someTable.AsEnumerable()
                    .Where(dataRow => dataRow.Field<int>("field_with_id") == someIntIdVariable)
                    .Select(dataRow => dataRow.Field<string>("field_with_string"))
                    .DefaultIfEmpty("НЕ НАЙДЕНО").Single();

C#, Interop.Word - ошибка "Отсутствует доступ к отдельным строкам, поскольку таблица имеет ячейки, объединенные по вертикали."

Или в английском варианте Cannot access individual rows in this collection because the table has vertically merged cells. Как несложно догадаться ошибка возникает при попытке объединения ячеек в таблице документа Word по вертикали, в колонках, точнее говоря при попытке обратиться к ячейкам в таблице где хоть в одной колонке уже произошло вертикальное объединения ячеек. Я поймал ее при попытке последовательно объединять ячейки в разных колонках, другие ловят при банальном чтении таблицы.

Судя по всему это глюк в интерфейсе работы с таблицами Word. Обойти его можно отказавшись от обращения к строкам таблицы и обращаясь напрямую к ячейкам через метод Table.Cell. Например при обьединении прямоугольного массива ячеек можно внести следующие изменения в код

// старый код обращался к ячейке через коллекцию строк //_table.Rows[cellOneRowIndex].Cells[cellOneColIndex]. Merge(_table.Rows[cellTwoRowIndex]. Cells[cellTwoColIndex]);

//новый обращается через общий массив ячеек
_table.Cell(cellOneRowIndex, cellOneColIndex). Merge(_table.Cell(cellTwoRowIndex, cellTwoColIndex));

Как изменить ориентацию страницы в MS Word из C# (interop)

Чтобы просто изменить ориентацию, достаточно использовать свойство диапазона-range PageSetup.Orientation принимающее значения WdOrientation

_currentRange.PageSetup.Orientation = Word.WdOrientation.wdOrientLandscape;

Но если мы хотим изменить ориентацию не всего документа, а конкретных страниц, то перед страницей с измененной ориентацией надо вставить разрыв секции используя метод Range.InsertBreak с параметром из перечисления WdBreakType

_currentRange.InsertBreak(Word.WdBreakType.wdSectionBreakNextPage);

How to programatically change PAGE ORIENTATION in WORD Document from Section Forward using VBA or VB.NET 2005

Красивая строка даты из datetime в MS SQL Server 2005 и младше

Начиная с MS SQL Server 2008 появляется специальный тип данных для дат. В более ранних версиях можно использовать функцию convert с кучей разнообразных кодов. Памятка для красивой строки даты виды 01.01.2012

select getdate() as real_date, convert(varchar, getdate(), 104) as date_string 

CAST and CONVERT (Transact-SQL)

Простейшее диалоговое окно-вопрос (MessageBox) в WindowsForms

string caption = "Уничтожение мира";
string message = "Вы уверены, что хотите уничтожить мир?";
DialogResult result = MessageBox.Show(message, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question);

if (result == DialogResult.No)
{ 
	return; 
}

Работаем с MS Word из C#, часть 4. Обьединяем несколько файлов в один, считаем количество страниц

Задача: сделать большой документ Word, состоящий из множества более мелких, заполняемых по шаблону (чтобы пользователю было удобно смотреть/печатать). Каждый поддокумент должен начинаться с новой страницы, длина каждого документа (даже в страницах) заранее не известна, но ее нужно считать и выводит.

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

Посчитать количество страниц внутри документа можно с использованием метода ComputeStatistics интерфейса _Document и перечисления WdStatistic

int pagesCount = 0;
Word.WdStatistic pagesStatType = Word.WdStatistic.wdStatisticPages;
pagesCount = _document.ComputeStatistics(pagesStatType, ref _missingObj);

Документ из файла можно вставить используя метод InsertFile

_currentRange.InsertFile(pathToFile);

Читать далее

Работаем с MS Word из C#, часть 3. Работа с таблицами

Выбрать уже существующую таблицу внутри документа можно по ее порядковому номеру (начиная с 1 и начала документа) можно через интерфейс Tables. При этом мы получим объект типа Table

Word.Table _table = _document.Tables[tableNumber];

Новая вставляется методом Tables.Add (предполагается что мы уже получили диапазон _currentRange того места в документе, куда будем ее вставлять):

_table = _document.Tables.Add(_currentRange, numRows, numColumns, ref _missingObj, ref _missingObj);

и добавить к ней строки

_table.Rows.Add(ref _missingObj);

Читать далее

Работаем с MS Word из C#, часть 2. Вставляем текст на закладку и форматируем

Продолжение былинной саги про Office.Interop.Word. Самый простой способ вставить кусок текста в нужную часть Word-шаблона - использовать закладки. В отличие от текстовых меток закладки не надо искать, они невидимы и всегда имеют уникальное имя. Имея открытый в предыдущей части документ Word мы можем с легкостью получить диапазон-Range для закладки (bookmark).

Word.Range bookmarkRange = _document.Bookmarks.get_Item(ref bookmarkNameObj).Range;

Кроме банальной вставки текста хотелось бы его немного поформатировать - жирный/курсив, размер текста, выравнивание абзаца. Все это доступно через интерфейс Range.
Читать далее

Работаем с MS Word из C#, часть 1. Открываем шаблон, ищем текст внутри документа

Задача: вывести данные в документ Word. На самом деле это очень большая и необъятная тема, примерно как сам Word, 90% возможностей которого не используются обычными пользователями. Сузим до более простой и чаще встречающейся на практике задачи, с которой в своей время пришлось столкнуться мне самому: надо вывести красивую справку, договор, отчет или иной документ Word с добавлением данных из кода C#. Само собой должны поддерживаться версии Word до 2007, так что о новых форматах файлов придется забыть.
Читать далее