Trong một số trường hợp phát triển ứng dụng WPF, có nhu cầu ẩn đi tùy chọn "Ghim vào thanh tác vụ" trong menu chuột phải trên biểu tượng cửa sổ ở thanh tác vụ. Điều này giúp kiểm soát trải nghiệm người dùng, đặc biệt khi ứng dụng mang tính tạm thời hoặc không nên được ghim như một ứng dụng chính.
Một ví dụ thực tế là cửa sổ của phần mềm như WeChat Official Account Platform, nơi chỉ hiển thị tùy chọn "Đóng cửa sổ" mà không có mục "Ghim vào thanh tác vụ". Tính năng này có thể đạt được thông qua API hệ thống Windows bằng cách thiết lập thuộc tính System.AppUserModel.PreventPinning.
Giới thiệu về System.AppUserModel.PreventPinning
Theo tài liệu chính thức của Microsoft, thuộc tính System.AppUserModel.PreventPinning cho phép vô hiệu hóa chức năng ghim cửa sổ hoặc lối tắt vào thanh tác vụ và menu Bắt đầu. Đồng thời, nó cũng ngăn ứng dụng xuất hiện trong danh sách "Hay dùng" của menu Bắt đầu.
Triển khai trong WPF
Để sử dụng thuộc tính này, ta cần làm việc với các API Win32 thông qua tương tác COM và P/Invoke. Cụ thể, sẽ sử dụng hàm SHGetPropertyStoreForWindow từ shell32.dll để truy cập IPropertyStore của cửa sổ và đặt giá trị cho khóa thuộc tính tương ứng.
Bước 1: Khai báo các cấu trúc và giao diện cần thiết
[DllImport("shell32.dll")]
internal static extern int SHGetPropertyStoreForWindow(IntPtr hwnd, ref Guid iid, out IPropertyStore propertyStore);
[DllImport("ole32.dll")]
internal static extern int PropVariantClear(ref PropVariant propVariant);
[ComImport]
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPropertyStore
{
void GetCount(out uint cProps);
void GetAt(uint iProp, out PropertyKey pkey);
void GetValue(ref PropertyKey key, out PropVariant pv);
void SetValue(ref PropertyKey key, ref PropVariant pv);
void Commit();
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct PropertyKey
{
public Guid FormatId;
public uint PropertyId;
public PropertyKey(Guid formatId, uint propertyId)
{
FormatId = formatId;
PropertyId = propertyId;
}
}
[StructLayout(LayoutKind.Explicit)]
internal struct PropVariant
{
[FieldOffset(0)] public ushort VariantType;
[FieldOffset(8)] public byte BooleanValue;
public void SetBoolean(bool value)
{
VariantType = 11; // VT_BOOL
BooleanValue = value ? (byte)0xFF : (byte)0x00;
}
}
Bước 2: Thiết lập thuộc tính PreventPinning khi cửa sổ khởi tạo
Trong lớp cửa sổ WPF (ví dụ: MainWindow), ta cần lấy handle cửa sổ sau khi nó được khởi tạo và gọi hàm thiết lập:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SourceInitialized += OnWindowSourceInitialized;
}
private void OnWindowSourceInitialized(object sender, EventArgs e)
{
DisableTaskbarPinning();
}
private void DisableTaskbarPinning()
{
var windowHandle = new WindowInteropHelper(this).Handle;
// Khóa thuộc tính cho PreventPinning
var preventPinningKey = new PropertyKey(
new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 9);
// IID của IPropertyStore
var propertyStoreGuid = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99");
IPropertyStore propertyStore;
var hr = SHGetPropertyStoreForWindow(windowHandle, ref propertyStoreGuid, out propertyStore);
if (hr != 0)
{
Marshal.ThrowExceptionForHR(hr);
}
try
{
var propValue = new PropVariant();
propValue.SetBoolean(true);
propertyStore.SetValue(ref preventPinningKey, ref propValue);
propertyStore.Commit();
PropVariantClear(ref propValue);
}
finally
{
if (propertyStore != null)
Marshal.ReleaseComObject(propertyStore);
}
}
}
Kết quả
Sau khi triển khai thành công, khi người dùng nhấn chuột phải vào biểu tượng ứng dụng trên thanh tác vụ, mục "Ghim vào thanh tác vụ" sẽ không còn hiển thị — tương tự như hành vi của một số cửa sổ hệ thống hoặc ứng dụng nhất định như WeChat PC.