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

11.10.2019

C#: Running a program in a single instance

Sometimes you need to run your program just in a single instance. For example it can happen when you use some resources which can not be shared (devices, databases or something else). In this case the following method of running your program can help:
class Program
{
     static void Main(string[] args)
     {
          bool existed;
          // obtaining application GIUD
          string guid = Marshal.GetTypeLibGuidForAssembly(Assembly.GetExecutingAssembly()).ToString();

          Mutex mutexObj = new Mutex(true, guid, out existed);

          if (existed)
          {
               Console.WriteLine("Application is already running");
          }
          else
          {
               Console.WriteLine("This is the first instance of this application.");
          }

          Console.ReadLine();
     }
}

04.09.2019

C#: Поиск элементов в списке строк без учета регистра

Небольшое, но полезное расширение для List<string>, позволяющее осуществлять поиск элементов в списке строк без учета регистра:
public static bool Contains(this string target, string value, StringComparison comparison)
{
    return target.IndexOf(value, comparison) >= 0;
}

09.11.2018

C#: Реализация Many-To-Many в Entity Framework 6

Пускай у нас будут две модели Articles (статьи) и Authors (авторы статей). У статей может быть несколько авторов, а авторы могут участвовать в написании нескольких статей. Ниже представлен шаблон для реализации конструкции связей Many-To-Many в Entity Framework 6, например, его можно использовать в рамках концепции Code-First для автоматического создания таблиц связей.

public class Article
{
    public int Id { get; set; }

    public string Title { get; set; }

    public string Surname { get; set; }

    public virtual ICollection Authors { get; set; }

    public Article()
    {
        Authors = new List();
    }
}

public class Author
{
    public int Id { get; set; }

    public string Fio { get; set; }

    public virtual ICollection  Articles { get; set; }
public Author() { Articles = new List();
} } public class Arctiles2AuthorsContext : DbContext { public DbSet Authors { get; set; }
public DbSet Articles { get; set; } public Arctiles2AuthorsContext() : base("DefaultConnection") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity().HasMany(c => c.Articles) .WithMany(s => s.Authors) .Map(t => t.MapLeftKey("ArticleId") .MapRightKey("AuthorId") .ToTable("Arctiles2Authors")); } }

12.09.2018

WPF: несколько полезных трюков

Недавно закончил работу над десктопным WPF-приложением. В нем я столкнулся с рядом задач, решение которых было не совсем очевидно, и я хочу ими поделиться.

1. Биндинг команд к событиям
Если разработчик хочет придерживаться MVVM модели, то для обработки событий он не сможет использовать стандартный обработчик команды в codebehind его xaml-формы. Для того, чтобы привязать событие и команду из ViewModel, он может использовать трюк, который предлагает JonghoL. В этом случае вам нужно просто написать следующий код:


Здесь добавляется пространство имен eb, в котором реализован класс EventBinding, который, в свою очередь может получать на вход название команды из ViewModel и возвращать в неё параметр.


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

public sealed class StringToBitmapImageConverter : ValueConverterBase
{
    public override object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        object result = null;

        if (value is string uri)
        {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri(uri);
            image.EndInit();
            result = image;
        }

        return result;
    }
    
    public override object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}


3. Создание пунктирной рамки 
В WPF нет стандартной кисти для реализации пунктирной линии границы. Для реализации такого стиля существуют разные подходы, в т.ч. с использованием codebehind. Приведу тот, который нравится мне, при необходимости его можно модифицировать под конкретную задачу:
<LinearGradientBrush x:Key="CropRectangleAreaBorderBrush" 
                     MappingMode="Absolute"
                     SpreadMethod="Repeat"
                     StartPoint="0,0"
                     EndPoint="30,30">
    <GradientStop Offset="0" Color="#40009299" />
    <GradientStop Offset="0.5" Color="#40169E4C" />
    <GradientStop Offset="0.501" Color="Transparent" />
</LinearGradientBrush>


PS. Функциональность, представленная в пунктах 1 и 2, а также множество других полезных классов и методов, доступна в разрабатываемом мной фреймворке на github, а такжк в nuget-пакетах: Ak.Framework.Wpf и Ak.Framework.Core.

15.05.2018

Project Server: ожидание выполнения задачи в очереди

В прошлом году делал внутренний проект по автоматизации работы с корпоративным Project Server. Весьма интересное занятие с учетом того, что некоторые вещи не так однозначны, и не всегда поведение системы соответствует описанному в документации. В процессе работы методом проб и ошибок было получено большое количество наработок, которые могут быть полезны. Вот, например, способ реализации ожидания выполнения задачи в очереди
private void WaitForQueue(Guid jobId)
{
   bool jobDone = false;
   int wait = ServicesManager.QueueSystemClient.GetJobWaitTime(jobId);

   Thread.Sleep(wait * 1000);
   do
   {
      string errorString;
      JobState jobState = ServicesManager.QueueSystemClient.GetJobCompletionState(jobId, out errorString);

      if (jobState == JobState.Success)
      {
         jobDone = true;
      }
      else
      {
         if (jobState.IsInEnumsList(JobState.Unknown, JobState.Failed, 
            JobState.FailedNotBlocking, JobState.CorrelationBlocked, JobState.Canceled))
         {
     throw new Exception("Queue request " + jobState + 
               " for Job ID " + jobId + ".\r\n" + errorString);
         }
         else
  {
     Thread.Sleep(2000);
  }
      }
   } while (!jobDone);
}
Здесь jobId - идентификатор работы, ServicesManager - контейнер подключений к службам служб Project Server (PSI).

03.01.2017

WPF: Error- "Default value type does not match type of property..."

At last I have my holidays and some spare time to write more posts to my blog. :)

When trying to add dependency property of type double you can easily get the error "Default value type does not match type of property..." with such code:
public double SomeDoubleValue
{
    get { return (double)GetValue(SomeDoubleValueProperty); }
    set { SetValue(SomeDoubleValueProperty, value); }
}
 
public static readonly DependencyProperty SomeDoubleValueProperty=
    DependencyProperty.Register("SomeDoubleValue", typeof(double),
    typeof(ownerclass), new PropertyMetadata(0));

The problem here is that "0" is not casted as double, that is why you should cast it explicitly like
public static readonly DependencyProperty SomeDoubleValueProperty=
    DependencyProperty.Register("SomeDoubleValue", typeof(double), 
    typeof(ownerclass), new PropertyMetadata(0D));

Some more information can be found here.

27.07.2014

C#: Получение даты сборки

К сожалению нет простого способа для получения даты выполнения билда .net-сборки, тем не менее наиболее точное значения можно получить с помощью заголовка PE-файла, в который при подликовке проставляется дата:
public static DateTime GetBuildDate()
{
   string filePath = Assembly.GetCallingAssembly().Location;
   const int peHeaderOffset = 60;
   const int linkerTimestampOffset = 8;
   byte[] b = new byte[2048];
   using (Stream s = new FileStream(filePath, FileMode.Open, FileAccess.Read))
   {
      s.Read(b, 0, 2048);
   }

   int secondsSince1970 = BitConverter.ToInt32(b, BitConverter.ToInt32(b, peHeaderOffset) + linkerTimestampOffset);
   DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
   dt = dt.AddSeconds(secondsSince1970);
   dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
   return dt;
}

03.02.2014

DotNetZip: решение проблемы с русскими названиями файлов

Столкнулся с проблемой некорректной распаковки файлов с названиями на кириллице с помощью библиотеки DotNetZip (Ionic.Zip). При распаковке файлов названия превращались в "крякозяблики". Решение оказалось довольно простым. Для открытия zip-файла, который мы собираемся распаковывать, используем следующий код:

ZipFile zip = ZipFile.Read(inputStream, new ReadOptions()
              {
                 Encoding = Encoding.GetEncoding("cp866")
              });

18.08.2013

Aspose: эскейп-последовательности при преобразовании текстового файла в pdf

Как было показано в предыдущем посте, можно заменять в word-файлах строки с помощью компонентов Aspose. При этом строка, на которую вы заменяете исходную, может содержать эскейп-последовательности. При выгрузке в получившегося файла в word-формате он будет, как и предполагается, с переносами, табуляциями и т.п. Однако, если вы попытаетесь программно сохранить и открыть файл в pdf-формате, вы обнаружите, что эскейп-символы отобразились неправильно. В старых версиях библиотек Aspose, они отображаются в виде квадратиков (об этом можно почитать здесь), в новых версиях проблема до конца не решена, и все эскейп-символы заменяются пробелами.
Метод проб и ошибок был найден простой способ борьбы с этой проблемой:
ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
{
   e.Replacement = e.Replacement.Replace("\n", ControlChar.LineBreak).Replace("\t", ControlChar.Tab);
   return ReplaceAction.Replace;
}
Таким же образом можно заменить и другие символы.

Aspose: условная замена строки при поиске

В своей работе часто приходится использовать компоненты Aspose. Это набор библиотек, прежде всего предназначенных для работы с файлами. Они позволяют легко работать с такими форматами, как doc, docx, rtf, pdf, xls, xlsx, ppt, pptx и многие другие.
Недавно возникла задача по условной замене строки в rtf-файле. Т.е. если искомая по маске строка имеет один вид, то заменяет на одну строку, если другой вид - то на другую. Делается это следующим образом:
Document doc = new Document(someFilenameOrStream);
doc.Range.Replace(new Regex(someRegexString), new ReplaceEvaluator(), false);
Здесь осуществляется поиск по регулярному выражению someRegexString. Каждый раз, когда программа находит строку в файле, удовлетворяющую условию, срабатывает обработчик ReplaceEvaluator:
public class ReplaceEvaluator : IReplacingCallback
{
   public ReplaceEvaluator()
   {
       // При необходимости можно передавать в конструктор параметры
   }

   ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
   {
       string match = e.Match.ToString();
       case (match)
       {
          // Логика обработки
       }

       return ReplaceAction.Replace;
   }
}

13.02.2013

Ограничения UpdatePanel

UpdatePanel не поддерживает в полной мере работу со следующими контролами (подробнее про условия применимости можно прочитать здесь):

  • TreeView
  • Menu
  • FileUpload
  • HtmlInputFile
  • GridView
  • DetailsView
  • Login
  • PasswordRecovery
  • CahngePassword
  • CreateUserWizard
  • Substitution

09.02.2013

SQL: Передача массива значений в хранимую процедуру

Существуют различные методы для того, чтобы передать в хранимую процедуру массив параметров. Я остановился на следующем варианте (дальнейшее описание верно для MS SQL Server 2008 и выше).

Для начала сериализуем список входных параметров (в качестве примера используем целочисленные значения). С целью упрощения задачи пишем расширение к классу List:
    public static class ListExtensions
    {
        /// Приведение к строковому представлению xml
        public static string ToXmlString(this List list)
        {
            XmlSerializer xs = new XmlSerializer(typeof(List));
            MemoryStream ms = new MemoryStream();
            xs.Serialize(ms, list);

            return Encoding.UTF8.GetString(ms.ToArray());
        }
    }
Теперь готовим массив
List list = new List();
list.Add(100);
list.Add(1000);
list.Add(10000);

string xmlString = list.ToXmlString();
xmlString - это наше входное значение для хранимой процедуры, в данном примере оно будет равно:
<?xml version="1.0"?>
<ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <int>100</int>
  <int>1000</int>
  <int>10000</int>
</ArrayOfInt>
Теперь сделаем sql-функцию, которая упростит нам жизнь. С помощью неё мы будем преобразовывать xml в таблицу с одним целочисленным столбцом:
CREATE FUNCTION f_GenerateIntTableFromXml
(
 @Xml xml
)
RETURNS @Table TABLE
(
 IntValue nvarchar(max)
)
AS
BEGIN
 INSERT INTO @Table (IntValue) 
 SELECT ParamValues.IntValue.value('.', 'int')
 FROM @Xml.nodes('/*/int') as ParamValues(IntValue)
 RETURN
END
Теперь можно сделать хранимую процедуру, которая будет принимать на вход список целочисленных параметров в xml-представлении (наша переменная xmlString):
CREATE PROCDEURE someProc
(
 @Values xml
)
AS
INSERT INTO tblSomeTable
SELECT i.IntValue, /* Ещё значения */
FROM dbo.f_GenerateIntTableFromXml(@Values) as i

LINQ: Быстрое приведение списка одного типа к другому

Пример приведения списка одного типа к другому:
List class1List = new List();
/* заполняем список */

List = class1List.Select(r => new Class2(){ Id = r.Id /*заполняем другие свойства*/}).ToList();

03.02.2013

Форматированная строка на основе свойств объекта

Понадобилось сделать форматированную строку, которая бы заполнялась на основе свойств объекта. Т.е. на входе подается строка вида "{Name}-{Number}: {Annotation}", поля в фигурных скобках должны заполниться на основе свойств заданного объекта. Получилось следующее:
public class SomeClass
{
   #region Свойства

   // Здесь задаем свойства класса

   #endregion

   public string ToFormatedString(string format)
   {
      string result = format;

      foreach (var prop in GetType().GetProperties())
         result = result.Replace(string.Format("{{{0}}}", prop.Name), 
                     prop.GetValue(this, null) != null
                        ? prop.GetValue(this, null).ToString()
                        : string.Empty);
      return result;
   }
}
Для пущего удобства данный код можно оформить в виде Extension или базового класса, от которого дальше проводить наследование.

28.01.2013

Расширения для LINQ - morelinq. Как написать расширение LINQ DistinctBy.

Библиотека методов LINQ, которых так не хватало - morelinq.
В частности, очень пригодился DistinctBy.
Тем не менее, DistinctBy очень просто реализовать:
public static IEnumerable<T> DistinctBy<T, K> (this IEnumerable<T> obj, Func<T, K> keySelector)
{
 HashSet<K> keys = new HashSet<K>();
 foreach (T element in obj)
  if (keys.Add(keySelector(element)))
   yield return element;
}