示例#1
0
        private void initUI()
        {
            double pnlWidth = (double)AppLib.GetAppGlobalValue("categoriesPanelWidth");
            double pnlHeight = (double)AppLib.GetAppGlobalValue("categoriesPanelHeight");
            double promoFontSize, dH, d1, dKoefPortionCount;
            double pnlW, titleFontSize;
            // грид блюд
            double dishesPanelWidth  = (double)AppLib.GetAppGlobalValue("dishesPanelWidth");
            double dishesPanelHeight = (double)AppLib.GetAppGlobalValue("dishesPanelHeight");

            scrollDishes.Height = dishesPanelHeight; scrollDishes.Width = dishesPanelWidth;
            double dishNameFontSize, dishUnitFontSize;
            string backgroundImage;
            Style  stl; Setter str;

            // дизайн вертикальный: панель меню СВЕРХУ
            if (AppLib.IsAppVerticalLayout)
            {
                DockPanel.SetDock(gridMenuSide, Dock.Top);
                //                menuSidePanelLogo.Background = (Brush)AppLib.GetAppGlobalValue("appBackgroundColor");
                // грид меню
                //pnlHeight *= 0.8;
                gridMenuSide.Height = pnlHeight;
                gridMenuSide.Width  = pnlWidth;

                // панель меню на всю ширину экрана
                dH = pnlHeight / 10d;
                gridMenuSide.RowDefinitions[0].Height = new GridLength(3.0 * dH);
                gridMenuSide.RowDefinitions[1].Height = new GridLength(0.0 * dH);
                gridMenuSide.RowDefinitions[2].Height = new GridLength(3.5 * dH);
                gridMenuSide.RowDefinitions[3].Height = new GridLength(0.0 * dH);
                gridMenuSide.RowDefinitions[4].Height = new GridLength(0.0 * dH);
                gridMenuSide.RowDefinitions[5].Height = new GridLength(3.5 * dH);

                // stackPanel для Logo
                gridMenuSide.Children.Remove(imageLogo);
                StackPanel pnlLogo = new StackPanel();
                pnlLogo.Orientation = Orientation.Horizontal;
                pnlLogo.Background  = new SolidColorBrush(Color.FromRgb(0x62, 0x1C, 0x55));

                pnlW = gridMenuSide.Width - 2.0 * dH;
                // перенести кнопку Назад
                gridMenuSide.Children.Remove(btnReturn);
                pnlLogo.Children.Add(btnReturn);
                btnReturn.Width = 0.3 * pnlW;  // общая ширина = ширина элемента + отступы справа/слева
                txtReturn.HorizontalAlignment = HorizontalAlignment.Left;
                btnReturn.Margin     = new Thickness(dH, 0, 0, 0);
                btnReturn.Background = pnlLogo.Background;
                //   языковые кнопки
                gridMenuSide.Children.Remove(gridLang);
                gridLang.Height = 2.0 * dH;  // необходимо для расчета размера внутренних кнопок
                gridLang.Width  = 0.2 * pnlW;
                gridLang.Margin = new Thickness(0.1 * pnlW, 0, 0.1 * pnlW, 0);
                pnlLogo.Children.Add(gridLang);
                gridLang.Visibility = Visibility.Visible;
                // языковые кнопки, фон для внешних Border, чтобы они были кликабельные
                btnLangUa.Background = pnlLogo.Background;
                btnLangRu.Background = pnlLogo.Background;
                btnLangEn.Background = pnlLogo.Background;
                double dMin      = Math.Min(gridLang.Height, gridMenuSide.Width / (0.3 + 1.0 + 0.3 + 1.0 + 0.3 + 1.0 + 0.3));
                double dLangSize = 0.7 * dMin;
                setLngInnerBtnSizes(btnLangUaInner, lblLangUa, dLangSize);
                setLngInnerBtnSizes(btnLangRuInner, lblLangRu, dLangSize);
                setLngInnerBtnSizes(btnLangEnInner, lblLangEn, dLangSize);

                // перенести промокод
                gridMenuSide.Children.Remove(gridPromoCode);
                gridPromoCode.ColumnDefinitions[3].Width = new GridLength(0.0 * dH);
                gridPromoCode.Width = 0.3 * pnlW;
                gridPromoCode.HorizontalAlignment = HorizontalAlignment.Right;
                gridPromoCode.Height = 1.5 * dH;
                promoFontSize        = 0.5 * dH;
                pnlLogo.Children.Add(gridPromoCode);

                Grid.SetRow(pnlLogo, 0);
                gridMenuSide.Children.Add(pnlLogo);

                // строка с общей стоимостью
                pnlTotal.Orientation   = Orientation.Horizontal;
                txtOrderPrice.Margin   = new Thickness(20, 0, 0, 0);
                pnlTotalLabel.FontSize = (double)AppLib.GetAppGlobalValue("appFontSize2");
                txtOrderPrice.FontSize = (double)AppLib.GetAppGlobalValue("appFontSize1");

                // кнопка Оформить
                btnPrintCheck.Margin       = new Thickness(dH, 0.0 * dH, dH, 0.8 * dH);
                btnPrintCheck.CornerRadius = new CornerRadius((double)AppLib.GetAppGlobalValue("cornerRadiusButton"));
                txtPrintCheck.FontSize     = (double)AppLib.GetAppGlobalValue("appFontSize4");
                txtPrintCheck.FontWeight   = FontWeights.Bold;
                gridMenuSide.Children.Remove(txtCashier);
                pnlPrintCheck.Children.Add(txtCashier);
                txtCashier.Style = (Style)this.Resources["goToCashierVer"];

                // фон
                backgroundImage = AppLib.GetImageFullFileName((string)AppLib.GetAppGlobalValue("BackgroundImageVertical"));

                setLangButtonStyle(true);   // "включить" текущую языковую кнопку

                // панели блюд
                dishBorderHeight  = (dishesPanelHeight - dishesListTitle.ActualHeight) / 6.0;
                titleFontSize     = 0.015 * dishesPanelHeight;
                dishNameFontSize  = 0.09 * dishBorderHeight;
                dishUnitFontSize  = 0.08 * dishBorderHeight;
                dKoefPortionCount = 1.5d;
                // отступы кнопок изменения кол-ва блюда
                setStylePropertyValue("dishPortionImageStyle", "Margin", new Thickness(0.03 * dishBorderHeight));
                setStylePropertyValue("dishDelImageStyle", "Margin", new Thickness(0.1 * dishBorderHeight));
            }

            // дизайн горизонтальный: панель меню слева
            else
            {
                DockPanel.SetDock(dockMain, Dock.Left);

                // грид меню
                gridMenuSide.Height = pnlHeight;
                gridMenuSide.Width  = pnlWidth;
                dH = pnlHeight / 13d;

                // промокод
                gridPromoCode.Height = 0.6 * dH;
                gridPromoCode.Margin = new Thickness(0, 0, 0, 0.4 * dH);
                promoFontSize        = 0.3 * gridPromoCode.Height;

                txtCashier.Margin = new Thickness(0.5 * dH, 0, 0.5 * dH, 0);

                // фон
                backgroundImage = AppLib.GetImageFullFileName((string)AppLib.GetAppGlobalValue("BackgroundImageHorizontal"));

                dishBorderHeight  = (dishesPanelHeight - dishesListTitle.ActualHeight) / 4.0;
                titleFontSize     = 0.02 * dishesPanelHeight;
                dishNameFontSize  = 0.09 * dishBorderHeight;
                dishUnitFontSize  = 0.08 * dishBorderHeight;
                dKoefPortionCount = 1.5;
                // отступы кнопок изменения кол-ва блюда
                setStylePropertyValue("dishPortionImageStyle", "Margin", new Thickness(0.03 * dishBorderHeight));
                setStylePropertyValue("dishDelImageStyle", "Margin", new Thickness(0.15 * dishBorderHeight));
            }

            // фон
            imgBackground.Source = ImageHelper.GetBitmapImage(backgroundImage);
            // яркость фона
            imgBackground.Opacity = (double)AppLib.GetAppGlobalValue("BackgroundImageBrightness", 0.3);

            // высота рамки блюда в заказе, из стиля
            setStylePropertyValue("dishBorderStyle", "MinHeight", dishBorderHeight);
            // элементы в строке блюда, относительно ее высоты dishBorderHeight
            // внутренние поля в рамке блюда
            setStylePropertyValue("dishItemStyle", "Padding", new Thickness(0.06 * dishBorderHeight, 0.05 * dishBorderHeight, 0, 0.05 * dishBorderHeight));

            // изображение блюда (1:1.33)
            setStylePropertyValue("dishImageBorderStyle", "Height", 1.0 * dishBorderHeight);
            setStylePropertyValue("dishImageBorderStyle", "Width", 1.33 * dishBorderHeight);

            //  наименование блюда
            setStylePropertyValue("dishNameStyle", "FontSize", dishNameFontSize);
            // ед.изм. блюда
            setStylePropertyValue("dishUnitStyle", "FontSize", dishUnitFontSize);
            // маркеры блюда
            setStylePropertyValue("dishMarksItemStyle", "Height", 1.2 * dishUnitFontSize);

            // заголовок ингредиентов
            setStylePropertyValue("dishIngrTitleStyle", "FontSize", dishUnitFontSize);
            // наименование и цена ингредиента
            setStylePropertyValue("dishIngrStyle", "FontSize", dishUnitFontSize);
            setStylePropertyValue("dishIngrDelImageStyle", "Width", 2.5 * dishUnitFontSize);
            setStylePropertyValue("dishIngrDelImageStyle", "Padding", new Thickness(0.5 * dishUnitFontSize, 0.2 * dishUnitFontSize, 0.5 * dishUnitFontSize, 0.2 * dishUnitFontSize));
            // количество и цена порции
            setStylePropertyValue("dishPortionTextStyle", "FontSize", dKoefPortionCount * dishNameFontSize);

            // промокод
            AppLib.SetPromocodeTextStyle(txtPromoCode);

            // яркость фона
            string opacity = AppLib.GetAppSetting("BackgroundImageBrightness");

            if (opacity != null)
            {
                imgBackground.Opacity = opacity.ToDouble();
            }

            // заголовок списка
            dishesListTitle.Margin   = new Thickness(0, titleFontSize, 0, titleFontSize);
            dishesListTitle.FontSize = 1.5 * titleFontSize;

            // большие кнопки скроллинга
            var v = Enum.Parse(typeof(HorizontalAlignment), (string)AppLib.GetAppGlobalValue("dishesPanelScrollButtonHorizontalAlignment"));

            btnScrollDown.Width  = (double)AppLib.GetAppGlobalValue("dishesPanelScrollButtonSize");
            btnScrollDown.Height = (double)AppLib.GetAppGlobalValue("dishesPanelScrollButtonSize");
            btnScrollDown.HorizontalAlignment = (HorizontalAlignment)v;
            btnScrollUp.Width  = (double)AppLib.GetAppGlobalValue("dishesPanelScrollButtonSize");
            btnScrollUp.Height = (double)AppLib.GetAppGlobalValue("dishesPanelScrollButtonSize");
            btnScrollUp.HorizontalAlignment = (HorizontalAlignment)v;
        }  // method
示例#2
0
        private FlowDocument createDocument(int width)
        {
            // создать объекты верхнего и нижнего колонтитулов
            XmlDocument xmlHeader = new XmlDocument();

            xmlHeader.Load(AppDomain.CurrentDomain.BaseDirectory + string.Format(@"PrinterBill\Header-{0}.xml", _langId));
            XmlDocument xmlFooter = new XmlDocument();

            xmlFooter.Load(AppDomain.CurrentDomain.BaseDirectory + string.Format(@"PrinterBill\Footer-{0}.xml", _langId));
            TextModel textHeader = new TextModel();
            TextModel textFooter = new TextModel();

            textHeader = DeSerialize <TextModel>(xmlHeader.OuterXml);
            textFooter = DeSerialize <TextModel>(xmlFooter.OuterXml);
            int       leftMargin     = getLineMargin("BillLineLeftMargin");
            Thickness lineMargin     = getLineMargin();
            Thickness lineMarginIngr = lineMargin;

            lineMarginIngr.Top = getLineMargin("BillLineIngrTopMargin");
            Thickness lineMarginPrice = lineMargin;

            lineMarginPrice.Top = getLineMargin("BillLinePriceTopMargin");

            var doc = new FlowDocument();

            doc.PageWidth = width;
            // значения по умолчанию
            doc.FontFamily = new FontFamily("Panton-Bold");
            doc.FontWeight = FontWeights.Normal;
            doc.FontStyle  = FontStyles.Normal;
            doc.FontSize   = Convert.ToInt32(AppLib.GetAppGlobalValue("BillLineFontSize", 12));

            // вставить изображение в заголовок
            addImageToDoc(textHeader, doc);
            // метка, если заказ С СОБОЙ
            if (_order.takeAway == true)
            {
                string langText = AppLib.GetLangTextFromAppProp("takeOrderOut");
                langText = string.Concat(" **** ", langText.ToUpper(), " ****");
                addParagraph(doc, langText, 1.5 * doc.FontSize, FontWeights.Bold, FontStyles.Normal, new Thickness(leftMargin, 20, 0, 10), TextAlignment.Center);
            }
            // добавить форматированный заголовок
            addSectionToDoc(textHeader, doc);

            // добавить строки заказа
            string  currencyName = AppLib.GetLangTextFromAppProp("CurrencyName");
            string  sAppSet;
            decimal totalPrice = 0; string itemName, stringRow;

            foreach (DishItem item in _order.Dishes)
            {
                // блюдо
                itemName = AppLib.GetLangText(item.langNames);
                // c гарниром?
                if ((item.SelectedGarnishes != null) && (item.SelectedGarnishes.Count > 0))
                {
                    DishAdding garn     = item.SelectedGarnishes[0];
                    string     garnName = AppLib.GetLangText(garn.langNames);
                    // 2017-02-02 Формирование полного наименования блюда с гарниром
                    // если DishFullNameInGargnish = true, то полное имя берем из гарнира,
                    // иначе к имени блюда добавляем имя гарнира
                    sAppSet = AppLib.GetAppSetting("DishFullNameInGarnish");
                    if (sAppSet != null && sAppSet.ToBool())
                    {
                        itemName = garnName;
                    }
                    else
                    {
                        itemName += " " + AppLib.GetLangTextFromAppProp("withGarnish") + " " + garnName;
                    }
                }
                //string stringRow = itemName.Substring(0, itemName.Count() > 30 ? 30 : itemName.Count());
                addParagraph(doc, itemName, doc.FontSize, doc.FontWeight, doc.FontStyle, lineMargin);

                // добавить ингредиенты
                if (item.SelectedIngredients != null)
                {
                    stringRow = "  + "; bool isFirst = true;
                    foreach (DishAdding ingr in item.SelectedIngredients)
                    {
                        itemName   = AppLib.GetLangText(ingr.langNames);
                        stringRow += ((isFirst) ? "" : "; ") + itemName;
                        isFirst    = false;
                    }
                    addParagraph(doc, stringRow, 0.9 * doc.FontSize, doc.FontWeight, FontStyles.Italic, lineMarginIngr);
                }

                // стоимость блюда
                decimal price       = item.GetPrice();
                string  priceString = string.Format("{0} x {1:0.00} {3} = {2:0.00} {3}", item.Count, price, item.Count * price, currencyName);
                addParagraph(doc, priceString, doc.FontSize, doc.FontWeight, doc.FontStyle, lineMarginPrice, TextAlignment.Right);
                totalPrice += item.Count * price;
            }
            // итог
            addTotalLine(doc, doc.FontSize, totalPrice, currencyName, leftMargin);

            // добавить форматированный "подвал"
            addSectionToDoc(textFooter, doc);
            // вставить изображение в "подвал"
            addImageToDoc(textFooter, doc);

            // печать штрих-кода
            string           bcVal13      = _order.BarCodeValue + BarCodeLib.getUPCACheckDigit(_order.BarCodeValue);
            Image            imageBarCode = BarCodeLib.GetBarcodeImage(bcVal13, (int)(1.2 * doc.PageWidth), 50);
            BlockUIContainer bcContainer  = new BlockUIContainer()
            {
                Child  = imageBarCode,
                Margin = new Thickness(leftMargin, 10, 0, 0)
            };

            doc.Blocks.Add(bcContainer);
            // вывести значение баркода в чек
            //string bcDisplay = string.Format("{0} {1} {2} {3}", bcVal13.Substring(0,2), bcVal13.Substring(2, 6), bcVal13.Substring(8, 4), bcVal13.Substring(12,1));
            string bcDisplay = string.Format("{0}  {1}  {2}", bcVal13.Substring(0, 1), bcVal13.Substring(1, 6), bcVal13.Substring(7));

            addParagraph(doc, bcDisplay, 0.75 * doc.FontSize, doc.FontWeight, doc.FontStyle, new Thickness(leftMargin, 5, 0, 0), TextAlignment.Center);

            return(doc);
        }
示例#3
0
        public bool CreateBill(out string msgBoxText)
        {
            bool retVal = true;

            msgBoxText = null;
            string userErrMsgSuffix = Environment.NewLine + AppLib.GetLangTextFromAppProp("userErrMsgSuffix");

            userErrMsgSuffix = userErrMsgSuffix.Replace("\\n", Environment.NewLine);

            AppLib.WriteLogTraceMessage("Создание пречека для заказа " + _order.OrderNumberForPrint.ToString());

            // свойства заказа, созадаваемые перед печатью чека:
            //      1. BarCodeValue - значение штрих-кода, 12 цифр (6 - yymmdd, 2 - код источника, 4 - номер чека для печати)
            //      2. LanguageTypeId - язык, который был выбран при создании чека (ua/en/ru)
            //--------------------------------------------------

            string deviceName = (string)AppLib.GetAppGlobalValue("ssdID", string.Empty);

            if (deviceName == string.Empty)
            {
                AppLib.WriteLogErrorMessage("В config-файле не найден элемент \"ssdID\" - идентификатор терминала самообслуживания.\n\t\tTrace: PrintBill.cs, CreateBill()");
                msgBoxText = "App config error: don't find 'ssdID' element." + userErrMsgSuffix;
                return(false);
            }
            if (deviceName.Length > 2)
            {
                deviceName = deviceName.Substring(0, 2);
            }

            // 1. OrderNumberForPrint
            if (_order.OrderNumberForPrint == -1)
            {
                AppLib.WriteLogErrorMessage("Класс PrintBill. Не указан номер заказа");
                msgBoxText = "App error: no order number." + userErrMsgSuffix;
                return(false);
            }

            // дату заказа создаем ПЕРЕД печатью
            _order.OrderDate = DateTime.Now;

            // 2. BarCodeValue
            //    идент.устройства - 2 символа
            if (deviceName.Length <= 2)
            {
                deviceName = string.Format("{0:D2}", deviceName);
            }
            else
            {
                deviceName = deviceName.Substring(0, 2);
            }
            //    дата заказа в формате yyMMdd - 6 символов
            //    номер заказа (для печати - случайный) в формате 0000 - 4 символа
            // т.к. в формате EAN-13 если первый символ - 0, то он удаляется, используем в начале дату
            string sBuf = (_order.OrderDate == null) ? "000000" : string.Format("{0:yyMMdd}", _order.OrderDate);

            _order.BarCodeValue = sBuf + _order.OrderNumberForPrint.ToString("000000");

            // 3. LanguageTypeId
            _order.LanguageTypeId = AppLib.AppLang;

            // ширина из config-файла
            int width = (int)AppLib.GetAppGlobalValue("BillPageWidht", 0);

            if (width == 0)
            {
                AppLib.WriteLogErrorMessage("В config-файле не указан элемент BillPageWidht с шириной чека. Берется значение по умолчанию - 300 (7,8см)");
                width = 300;
            }

            // принтеры в системе
            List <PrintQueue> printers = PrintHelper.getPrintersList();

            if (printers == null)
            {
                AppLib.WriteLogErrorMessage("В системе не зарегистрирован ни один принтер!!");
                msgBoxText = AppLib.GetLangTextFromAppProp("printConfigError");
                return(false);
            }
            else
            {
                try
                {
                    string sLog = string.Join(Environment.NewLine + "\t", printers.Select(pq => pq.Name + ", status '" + getPrinterStatus(pq) + "', driver '" + pq.QueueDriver.Name + "'"));
                    AppLib.WriteLogTraceMessage("Системные принтеры: " + Environment.NewLine + "\t" + sLog);
                }
                catch (Exception) { }
            }

            // имя принтера для печати чека
            string printerName = null;
            string result      = null;

            #region поиск принтера для печати чека
            printerName = AppLib.GetAppSetting("PrinterName");
            PrintQueue printer = PrintHelper.GetPrintQueueByName(printerName);
            if (printer == null)
            {
                AppLib.WriteLogErrorMessage("В config-файле не указан элемент PrinterName или в системе не найден принтер: " + printerName);
                printerName = null;
            }
            else
            {
                result = getPrinterStatus(printer);
                AppLib.WriteLogTraceMessage($"Принтер '{printerName}' находится в состоянии '{result}'");
            }

            // если принтер из настроек не Ок, то берем принтер по умолчанию
            if ((printer == null) || ((result != null) && (result != "OK")))
            {
                AppLib.WriteLogTraceMessage("Предпринимается попытка использовать принтер по умолчанию...");
                printer = PrintHelper.GetDefaultPrinter();
                if ((printer != null) &&
                    ((printerName == null) || ((printerName != null) && (printer.Name != printerName))))
                {
                    printerName = printer.Name;
                    result      = getPrinterStatus(printer);
                    AppLib.WriteLogTraceMessage($"Найден принтер по умолчанию: {printerName}");
                    AppLib.WriteLogTraceMessage($"Принтер '{printerName}' находится в состоянии '{result}'");
                }
            }

            // если принтер по умолчанию не ОК, то берем первый в системе
            if (printer == null)
            {
                AppLib.WriteLogTraceMessage("Предпринимается попытка использовать первый найденный принтер в ОС...");
                printer = PrintHelper.GetFirstPrinter();
                if ((printer != null) &&
                    ((printerName == null) || ((printerName != null) && (printer.Name != printerName))))
                {
                    printerName = printer.Name;
                    result      = getPrinterStatus(printer);
                    AppLib.WriteLogTraceMessage($"Найден первый принтер: {printerName}");
                    AppLib.WriteLogTraceMessage($"Принтер '{printerName}' находится в состоянии '{result}'");
                }
            }
            // принтер не найден - досвидос
            if (printer == null)
            {
                msgBoxText = "App print error: not found any printer." + userErrMsgSuffix;
                return(false);
            }
            // найден, но статус не ОК - досвидос
            else if ((result != null) && (result != "OK"))
            {
                string sFormat = AppLib.GetLangTextFromAppProp("printerStatusMsg");
                if (sFormat != null)
                {
                    sFormat = sFormat.Replace("\\n", Environment.NewLine);
                }
                msgBoxText = string.Format(sFormat, printerName, result) + userErrMsgSuffix;
                return(false);
            }
            #endregion

            // создание документа
            AppLib.WriteLogTraceMessage($" Создаю документ для печати...");
            FlowDocument doc = null;
            try
            {
                doc = createDocument(width);
                AppLib.WriteLogTraceMessage($" - документ создан успешно");
            }
            catch (Exception ex)
            {
                result = AppLib.GetLangTextFromAppProp("afterPrintingErrMsg");
                if (result != null)
                {
                    result = result.Replace("\\n", Environment.NewLine);
                }
                msgBoxText = result + userErrMsgSuffix;
                AppLib.WriteLogErrorMessage(" Ошибка формирования документа: " + ex.ToString());
                return(false);
            }

            // имя задания на принтер
            string prnTaskName = "bill " + _order.OrderNumberForPrint.ToString();
            // вывод документа на принтер
            AppLib.WriteLogTraceMessage($" Вывожу пречек на принтер...");
            retVal = PrintHelper.PrintFlowDocument(doc, prnTaskName, printerName, out msgBoxText);
            if (retVal == false)
            {
                // сообщение об ошибке в лог
                AppLib.WriteLogErrorMessage(" Ошибка печати документа: " + msgBoxText);
                // и на экран пользователю
                result = AppLib.GetLangTextFromAppProp("afterPrintingErrMsg");
                if (result != null)
                {
                    result = result.Replace("\\n", Environment.NewLine);
                }
                msgBoxText = result + userErrMsgSuffix;
            }
            else
            {
                AppLib.WriteLogTraceMessage("Пречек распечатан успешно");
                if (msgBoxText != null)
                {
                    AppLib.WriteLogErrorMessage(msgBoxText);
                }
            }

            return(retVal);
        }
示例#4
0
        // анимировать перемещение блюда в тележку
        private void animateDishSelection()
        {
//            AppLib.WriteLogTraceMessage("Выбор Вока: вычисление геометрии анимации");
            // перемещаемое изображение
            (animImage.Fill as VisualBrush).Visual = dishImage;
            //animImage.Fill = Brushes.Green;  // debug

            // обновление пути анимации
            PathFigure    pf        = (animPath.Data as PathGeometry).Figures[0];
            BezierSegment bezierSeg = (pf.Segments[0] as BezierSegment);
            // получить точку начала анимации: центр панели блюда
            Point fromPoint = dishImage.PointToScreen(new Point(dishImage.ActualWidth / 2d, dishImage.ActualHeight / 2d));

            if (AppLib.ScreenScale != 1d)
            {
                fromPoint = PointFromScreen(fromPoint);
            }
            Point toPoint = bezierSeg.Point3;

            pf.StartPoint = fromPoint;
            // и опорные точки кривой Безье
            double dX, dY; Point p1, p2;

            if (AppLib.IsAppVerticalLayout)
            {
                dX = fromPoint.X - toPoint.X;
                dY = fromPoint.Y - toPoint.Y;
                // блюдо справа
                if (dX > 0)
                {
                    p1 = new Point(0.5 * toPoint.X, 1.3 * fromPoint.Y);
                    p2 = new Point(0.8 * toPoint.X, 0.5 * fromPoint.Y);
                }
                // блюдо слева
                else
                {
                    p1 = new Point(1.2 * toPoint.X, 1.3 * fromPoint.Y);
                    p2 = new Point(1.2 * toPoint.X, 0.5 * fromPoint.Y);
                }
            }
            else
            {
                dX = fromPoint.X - toPoint.X;
                dY = toPoint.Y - fromPoint.Y;
                p1 = new Point(fromPoint.X - 0.3 * dX, 0.3 * fromPoint.Y);
                p2 = new Point(toPoint.X + 0.05 * dX, toPoint.Y - 0.8 * dY);
            }
            bezierSeg.Point1 = p1;
            bezierSeg.Point2 = p2;

//            AppLib.WriteLogTraceMessage("Выбор Вока: сделать видимой панель анимации");
            canvasAnim.Visibility = Visibility.Visible;

            // установить скорость анимации
//            AppLib.WriteLogTraceMessage("Выбор Вока: установить скорость анимации");
            double   animSpeed = double.Parse(AppLib.GetAppSetting("SelectDishAnimationSpeed")); // in msec
            TimeSpan ts        = TimeSpan.FromMilliseconds(animSpeed);

            foreach (Timeline item in _animDishSelection.Children)
            {
                item.Duration = ts;
            }
            // обновление стоимости заказа в анимациях
//            AppLib.WriteLogTraceMessage("Выбор Вока: старт анимации");
            _animDishSelection.Begin();
        }