Подтвердить что ты не робот

WPF RibbonWindow + Ribbon = название внешнего экрана?

Я пытаюсь управлять Ribbon в комбинации с RibbonWindow, однако они терпят неудачу даже в тривиальных экспериментах.

  • Создано новое приложение WPF
  • Изменен код пример из MSDN
  • Добавлена ​​ссылка на System.Windows.Controls.Ribbon и избавилась от префикса ribbon: (почему примеры устарели?).
  • Добавлено два значка (16x16 и 32x32).
  • Выполнено приложение и увидено это (Блокнот для справки):

Я уже вижу многочисленные проблемы:

  • Граница крошечная. Нормальное окно имеет большую границу, приложение WPF Ribbon имеет крошечный. Высота заголовка также меньше.
  • Граница размыта. Когда основное окно сфокусировано, граница становится черной. Граница приложения WPF является серой (черный можно увидеть в углах, что-то нарисовано над границами?).
  • Значок приложения неуместен. Он приклеился к верхнему левому углу.
  • Недопустимое название приложения. Он приклеен к верхней части.

Переместите панель инструментов вниз. Теперь мы видим следующее:

Кнопки находятся за пределами панели инструментов.

И наконец, позвольте максимизировать окно:

Половина заголовка исчезла за пределами экрана (технически окно выходит за пределы экрана на 8 пикселей с каждой стороны, но другие приложения не путаются этим).

Я использую Windows 7, Aero, один монитор, ничего особенного. Я боюсь тестировать приложение на Windows 8...

Есть ли возможность исправить это?

4b9b3361

Ответ 1

Реальная задача

Под капотом класс WindowChrome связывает его ResizeBorderThickness с SystemParameters.WindowResizeBorderThickness, который по очереди использует Win32 API GetSystemMetrics, чтобы определить размер границы системы.

Однако поведение этого метода изменяется в зависимости от версии подсистемы, установленной в исполняемом PE-заголовке. Если он скомпилирован только для Windows Vista и более поздних версий (версия >= 6.0), он будет возвращать более тонкие границы, чем если бы они были скомпилированы для более старых операционных систем. Подробнее об этом в этом ответе SO.

При компиляции с .NET 4.5 компилятор С# устанавливает эту версию в 6.0, поскольку .NET 4.5 нельзя использовать в XP. Тем не менее, класс WindowChrome, похоже, полагается на прежнее поведение и, следовательно, не может правильно рассчитать размер стекла в Windows Vista и 7.

Решение

Использовать .NET 4

Вы можете скомпилировать с .NET 4, чтобы заставить компилятор использовать 4.0 в качестве значения версии подсистемы. Лента доступна для WPF 4 как отдельная загрузка. Обратите внимание, что даже с этим решением вы должны снять флажок "Включить хостинг Visual Studio" в свойствах проекта для целей отладки. В противном случае будет использоваться процесс vshost.exe, который будет помечен версией подсистемы 6.0.

Измените версию подсистемы

Изменить: Olly предоставил способ сделать это в комментариях:

Добавить свойство в файл проекта <subsystemversion>5.01</subsystemversion>, что ложно указывает на то, что код может работать в Windows XP.

Игнорировать систему

Вы можете изменить прикрепленное свойство WindowChrome.WindowChrome в своем окне и использовать нужные значения, тем самым полностью игнорируя системные значения. Вы никогда не должны этого делать, но можете.

Заполните ошибку

Существует проблема с подключением к изменению поведения GetSystemMetrics, но все сводится к версии подсистемы, поэтому она скорее функция из точки зрения Microsoft. Тем не менее, класс WindowChrome действительно должен быть исправлен для корректной работы в Vista/7, тем более, что теперь он построен в .NET 4.5.

Ответ 2

Для тех, кто читает этот вопрос, я сам отвечаю. Забудьте об ужасном комплекте ленточного управления и используйте что-то еще. Посмотрите на некоторые из альтернатив здесь: Что представляет собой лучший комплект управления WPF Ribbon? (как и все хорошие вопросы, он закрыт, хотя).

До сих пор Fluent Ribbon Control Suite выглядит как лучший бесплатный вариант для меня. Базовая функциональность просто работает (никаких проблем с границами и максимизации, изменение размера окна не замедляется, как ад и т.д.). Он имеет стили Office и сохраняет их, если стекло отключено (это означает, что вы не увидите окно Windows9x-ish в Metro). Его интерфейс (backstage, QAT) больше похож на Office 2010.

Возможно, в каком-то отдаленном будущем Microsoft исправит свою ленту, но пока найдите альтернативы.

Ответ 3

Вот еще один WorkAround, очень простой и простой способ. Просто добавьте отрицательный запас на панель инструментов. Вам нужно сохранить оригинальный класс Window, а не RibbonWindow!

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Application Name" Height="350" Width="525" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged">

Просто добавьте это поле в заголовок ленты

<Ribbon Title="" Foreground="#333333" Margin="0,-22,0,0">

Теперь, когда вы максимизируете окно, все остается на месте.

Ответ 4

У меня была та же проблема с названием в RibbonWindow. Я решил это, установив глобальный стиль TextBlock в RibbonTitlePanel.

    <Style TargetType="{x:Type TextBlock}"> 
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type primitives:RibbonTitlePanel}},Path=Visibility}" Value="Visible"></Condition>
                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type RibbonWindow}},Path=WindowState}" Value="Maximized"></Condition>
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.Setters>
                <Setter Property="VerticalAlignment" Value="Center"></Setter>
            </MultiDataTrigger.Setters>
        </MultiDataTrigger>
    </Style.Triggers>
</Style>

Ответ 5

Это не решение, возможно, даже не работа, а скорее плохой хак, который я надеюсь использовать только на короткое время, пока проблема не будет исправлена ​​в рамках.

Код в основном копирует и вставляет из этого вопроса fooobar.com/questions/260161/...

Я изменил разрешенную позицию экрана, которая, похоже, помогает решить проблему, а не исправляет ее.

Звонок подобен этому в коде

        InitializeComponent();
        RibbonWindowService.FixMaximizedWindowTitle(this);


public static class RibbonWindowService
{
    public static void FixMaximizedWindowTitle(Window window)
    {
        window.SourceInitialized += WinSourceInitialized;
    }

    [DllImport("user32")]
    internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

    [DllImport("User32")]
    internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

    private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case 0x0024:
                WmGetMinMaxInfo(hwnd, lParam);
                handled = true;
                break;
        }

        return (IntPtr)0;
    }

    private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    {
        MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Adjust the maximized size and position to fit the work area of the correct monitor
        int MONITOR_DEFAULTTONEAREST = 0x00000002;
        IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

        if (monitor != IntPtr.Zero)
        {
            MONITORINFO monitorInfo = new MONITORINFO();
            GetMonitorInfo(monitor, monitorInfo);
            RECT rcWorkArea = monitorInfo.rcWork;
            RECT rcMonitorArea = monitorInfo.rcMonitor;

            // Offset top and left 1 pixel improves the situation
            rcMonitorArea.top += 1;
            rcMonitorArea.left += 1;

            mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
            mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
            mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
            mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
        }

        Marshal.StructureToPtr(mmi, lParam, true);
    }

    private static void WinSourceInitialized(object sender, EventArgs e)
    {
        IntPtr handle = (new WinInterop.WindowInteropHelper((Window)sender)).Handle;
        WinInterop.HwndSource.FromHwnd(handle).AddHook(WindowProc);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MINMAXINFO
    {
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        /// <summary>
        /// x coordinate of point.
        /// </summary>
        public int x;

        /// <summary>
        /// y coordinate of point.
        /// </summary>
        public int y;

        /// <summary>
        /// Construct a point of coordinates (x,y).
        /// </summary>
        public POINT(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public struct RECT
    {
        /// <summary> Win32 </summary>
        public int left;

        /// <summary> Win32 </summary>
        public int top;

        /// <summary> Win32 </summary>
        public int right;

        /// <summary> Win32 </summary>
        public int bottom;

        /// <summary> Win32 </summary>
        public static readonly RECT Empty = new RECT();

        /// <summary> Win32 </summary>
        public int Width
        {
            get { return Math.Abs(right - left); } // Abs needed for BIDI OS
        }

        /// <summary> Win32 </summary>
        public int Height
        {
            get { return bottom - top; }
        }

        /// <summary> Win32 </summary>
        public RECT(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }

        /// <summary> Win32 </summary>
        public RECT(RECT rcSrc)
        {
            left = rcSrc.left;
            top = rcSrc.top;
            right = rcSrc.right;
            bottom = rcSrc.bottom;
        }

        /// <summary> Win32 </summary>
        public bool IsEmpty
        {
            get
            {
                // BUGBUG : On Bidi OS (hebrew arabic) left > right
                return left >= right || top >= bottom;
            }
        }

        /// <summary> Return a user friendly representation of this struct </summary>
        public override string ToString()
        {
            if (this == Empty)
            {
                return "RECT {Empty}";
            }
            return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
        }

        /// <summary> Determine if 2 RECT are equal (deep compare) </summary>
        public override bool Equals(object obj)
        {
            if (!(obj is Rect))
            {
                return false;
            }
            return (this == (RECT)obj);
        }

        /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
        public override int GetHashCode()
        {
            return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
        }

        /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
        public static bool operator ==(RECT rect1, RECT rect2)
        {
            return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
        }

        /// <summary> Determine if 2 RECT are different(deep compare)</summary>
        public static bool operator !=(RECT rect1, RECT rect2)
        {
            return !(rect1 == rect2);
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class MONITORINFO
    {
        /// <summary>
        /// </summary>            
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

        /// <summary>
        /// </summary>            
        public RECT rcMonitor = new RECT();

        /// <summary>
        /// </summary>            
        public RECT rcWork = new RECT();

        /// <summary>
        /// </summary>            
        public int dwFlags = 0;
    }
}

Ответ 6

Вот мое решение. Я использую событие SizeChanged для обнаружения максимизированного состояния, после чего создаю поле для основной сетки.

private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    Thickness m = GridMain.Margin;

    if (WindowState == WindowState.Maximized)
    {
        m.Left = 3;
        m.Bottom = 3;
        m.Left = 3;
    }
    else
    {
        m.Left = 0;
        m.Bottom = 0;
        m.Left = 0;
    }
    GridMain.Margin = m;
}