UniversalApp(not UWP)作ってるとXAMLをSharedプロジェクトに突っ込んで、x:Nameつけて、VisualStateとか#ifdefで電話だったらVisibilityを消して…とかやるよね、やるよね?でもそれXAMLで書きたいよね?ということでXAMLで書いてみた。
using System.Collections.Generic; using System.Linq; using System.Reflection; using Windows.UI.Xaml; // behavior sdk 必要 using Microsoft.Xaml.Interactions.Core; namespace XamlConditions { // あやしげなTypeConverter public class TypeConverter { public static object ChangeType(Type targetType, object from) { var typeInfo = targetType.GetTypeInfo(); if (from == null) return typeInfo.IsValueType ? Activator.CreateInstance(targetType) : null; if (typeInfo.IsAssignableFrom(from.GetType().GetTypeInfo())) return from; // Convert var str = from.ToString(); return typeInfo.IsEnum ? Enum.Parse(targetType, str) : ConvertType(str, targetType.FullName); } private static object ConvertType(string from, string type) { try { // Get Internal method var typeName = typeof(EventTriggerBehavior).AssemblyQualifiedName .Replace(".EventTriggerBehavior,", ".TypeConverterHelper,"); var typeConverterHelperType = Type.GetType(typeName); var typeConverterHelperConvert = typeConverterHelperType .GetRuntimeMethod("Convert", new[] { typeof(string), typeof(string) }); return typeConverterHelperConvert.Invoke(null, new object[] { from, type }); } catch { return null; } } } // 本体 public class IsWindowsPhone { #region Attached Property public static string GetOverride(DependencyObject obj) { return (string)obj.GetValue(OverrideProperty); } public static void SetOverride(DependencyObject obj, string value) { obj.SetValue(OverrideProperty, value); } public static readonly DependencyProperty OverrideProperty = DependencyProperty.RegisterAttached("Override", typeof(string), typeof(IsWindowsPhone), new PropertyMetadata(default(string), OnOverrideStringChanged)); #endregion #region Previous Property Save Area private static Dictionary<string, object> GetPreviousPropertyBag(DependencyObject obj) { return (Dictionary<string, object>)obj.GetValue(PreviousPropertyBagProperty); } private static void SetPreviousPropertyBag(DependencyObject obj, Dictionary<string, object> value) { obj.SetValue(PreviousPropertyBagProperty, value); } public static readonly DependencyProperty PreviousPropertyBagProperty = DependencyProperty.RegisterAttached("PreviousPropertyBag", typeof(Dictionary<string, object>), typeof(IsWindowsPhone), new PropertyMetadata(null)); #endregion private static void OnOverrideStringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { #if WINDOWS_PHONE_APP UpdateProperty(d, e.NewValue as string); #endif } private static void UpdateProperty(DependencyObject d, string e) { RestorePreviousProperties(d); if (string.IsNullOrWhiteSpace(e)) return; var props = ParseAttributesString(e); SaveCurrentProperties(d, props.Keys.AsEnumerable()); // update values var type = d.GetType(); foreach (var kv in props) { var prop = type.GetRuntimeProperty(kv.Key); var value = TypeConverter.ChangeType(prop.PropertyType, kv.Value); prop.SetValue(d, value); } } private static void SaveCurrentProperties(DependencyObject d, IEnumerable<string> names) { var type = d.GetType(); var props = names.Select(s => type.GetRuntimeProperty(s)); var bag = props.ToDictionary(p => p.Name, p => p.GetValue(d)); SetPreviousPropertyBag(d, bag); } private static void RestorePreviousProperties(DependencyObject d) { var bag = GetPreviousPropertyBag(d); if (bag == null) return; var type = d.GetType(); var props = bag.Keys.Select(s => type.GetRuntimeProperty(s)); foreach (var p in props) { p.SetValue(d, bag[p.Name]); } SetPreviousPropertyBag(d, null); } private static Dictionary<string, string> ParseAttributesString(string attr) { var ret = new Dictionary<string, string>(); string name = null; foreach (var v in attr.Split('=')) { var n = v.LastIndexOf(","); if (n < 0) { if (name != null) ret.Add(name.Trim(), v); else name = v; continue; } ret.Add(name.Trim(), v.Substring(0, n)); name = v.Substring(n + 1); } return ret; } } }
これを適当にこんな感じで
<Grid xmlns:c="using:XamlConditions"> <!-- WindowsPhoneだけで見えるボタン --> <Button Visibility="Collapsed" c:IsWindowsPhone.Override="Visibility=Visible" /> </Grid>
すると電話だけででるボタンができます。がリフレクションしまくってるからあんまし早くないです。