Отметьте: Этот вопрос собрал хорошую сумму обратной связи, и некоторые люди ниже сделали большие точки, или фиксирует. Поэтому, в то время как я сохраню код здесь (и возможно обновлю его), я имею также , создал проект WpfAppBar на github. Не стесняйтесь отправлять запросы получения по запросу.
Тот же самый проект также создает к пакет WpfAppBar nuget
, я взял код из первой ссылки, предоставленной в вопросе ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ), и изменил его, чтобы сделать две вещи:
Этот подход не создает базовый класс.
Для использования просто звоните, этот код отовсюду в нормальном wpf окне (скажите нажатие кнопки или инициализирование). Обратите внимание, что Вы не можете назвать это, пока окно не инициализируется, если HWND еще не был создан (как в конструкторе), ошибка произойдет.
Делают окно appbar:
AppBarFunctions.SetAppBar( this, ABEdge.Right );
Восстановление окно к нормальному окну:
AppBarFunctions.SetAppBar( this, ABEdge.None );
Вот полный код в файл - примечание , Вы захотите изменить пространство имен на строке 7 к чему-то соответствующему.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary s_RegisteredWindowInfo
= new Dictionary();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if( s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition =new Point( appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size( appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if( edge == ABEdge.None)
{
if( info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = 0;
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = 0;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth);
}
else
{
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = 0;
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = 0;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight);
}
else
{
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
Я использую строго типизированные представления, когда могу, поэтому я могу уйти от всех приведений различных битов ViewData в представлении.
Я создаю свои собственные строго типизированные представления в любое время, когда мне нужно информация из более чем одного источника для представления.
Например, в моем оформлении заказа мне нужен заказ, а также настройки пользователя для отображения цены; поэтому я создал CheckoutViewModel, у которого есть свойства Order и PriceDisplay.
Надеюсь, что это поможет,
Дэн
Каждый раз, когда вам нужно отобразить данные (о любом конкретном объекте или коллекции объектов) в представлении, используйте строго типизированное представление.
Если ваше представление является чисто информационным, вы можете быть возможность использовать ModelState для передачи небольших фрагментов информации (например, страниц об успехах / ошибках, неавторизованных сообщений и т. д.)
В моих приложениях КАЖДЫЙ вид строго типизирован, так что я могу легко передавать информацию для входа в систему на главную страницу. То есть все мои представления строго типизированы, построены по шаблонам и ограничены базовым классом, который содержит конфигурацию сайта и информацию для входа в систему.
Благодаря этому я могу сделать это:
public class MyBaseMasterPage : ViewMasterPage<MyBaseModel>
{
public string CurrentTheme
{
get
{
if (this.Model.CurrentUser != null)
return this.Model.CurrentUser.Theme;
else return this.Model.Config.DefaultTheme;
}
}
public User CurrentUser { get { return this.Model.CurrentUser; } }
public ConfigurationRepository Config { get { return this.Model.Config; } }
}
Обратите внимание, что, поскольку Мастер Страница тематически основана ТОЛЬКО на том, что заполняется в модели, само представление никогда не вызовет попадание в базу данных / кеш.
MyBaseModel настроен следующим образом:
Если есть данные, должно быть строго типизированное представление. Период.
Всегда. Я бы пошел дальше и использовал строго типизированные элементы управления и действия HtmlHelper. Большинство из них доступно в библиотеке MvcContrib .
Строго типизированное представление лучше всего использовать, когда вы изменяете экземпляр этого типа в представлении.
Например, когда вы редактируете или создаете продукт в представлении, было бы определенно посоветовать иметь строго типизированное представление для класса продукта.
Если вы показываете только текст или изображения, фактически не имея связи с чем-либо на нижележащих уровнях базы данных, вероятно, будет проще отказаться от строго типизированного представления.
Это будет все получается довольно естественно, поскольку, по моему опыту, вы больше работаете с MVC.
Although I'm repeating what others have eloquently stated I think there is one more point to raise. The View is part of the Model, View, Controller concept and as such is there to present the Model in a visual way to the user. Given that it is, in essence, a representation of the Model it makes sense for it to be strongly typed.
I only ever use ModelState or TempState for passing small pieces of data, like outcome messages from activities like add, delete, etc. Any time I find tempted to use State to pass collections that are unrelated to the View-type then I refactor that functionality into a partial view and present that through a separate controller action.
In my code related types are generally referenced hierarchically from the base type to which the view is strongly-typed, and so are accessible within the View where necessary.