1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-11-23 22:30:09 +00:00

Development

This commit is contained in:
hawkeye 2024-02-27 21:44:21 -05:00
parent 380b3ca73e
commit 9d18b1fce4
154 changed files with 8942 additions and 1843 deletions

View file

@ -0,0 +1,11 @@
using MSFSPopoutPanelManager.DomainModel.Setting;
namespace MSFSPopoutPanelManager.DomainModel.DataFile
{
public class AppSettingFile
{
public string FileVersion { get; set; } = "4.0";
public ApplicationSetting ApplicationSetting { get; set; } = new();
}
}

View file

@ -5,14 +5,8 @@ namespace MSFSPopoutPanelManager.DomainModel.DataFile
{ {
public class UserProfileFile public class UserProfileFile
{ {
public UserProfileFile() public string FileVersion { get; set; } = "4.0";
{
FileVersion = "4.0";
Profiles = new List<UserProfile>();
}
public string FileVersion { get; set; } public IList<UserProfile> Profiles { get; set; } = new List<UserProfile>();
public IList<UserProfile> Profiles { get; set; }
} }
} }

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl> <PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.DomainModel</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.DomainModel</RootNamespace>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.0.3.4</Version> <Version>4.0.4.1</Version>
<AssemblyVersion>4.0.3.4</AssemblyVersion> <AssemblyVersion>4.0.4.1</AssemblyVersion>
<FileVersion>4.0.3.4</FileVersion> <FileVersion>4.0.4.1</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType> <DebugType>Embedded</DebugType>
<Configurations>Debug;Release;Local</Configurations> <Configurations>Debug;Release;Local</Configurations>

View file

@ -0,0 +1,11 @@
using MSFSPopoutPanelManager.Shared;
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
{
public class LodConfig : ObservableObject
{
public int Agl { get; set; }
public int Lod { get; set; }
}
}

View file

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Collections.Specialized;
namespace MSFSPopoutPanelManager.DomainModel.DynamicLod
{
public class ObservableLodConfigLinkedList : LinkedList<LodConfig>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, LodConfig item)
{
if (CollectionChanged == null)
return;
switch (action)
{
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset:
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
break;
}
}
public new void AddFirst(LodConfig item)
{
base.AddFirst(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
public new void AddLast(LodConfig item)
{
base.AddLast(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
}
public new void AddBefore(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
{
base.AddBefore(node, newNode);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
}
public new void AddAfter(LinkedListNode<LodConfig> node, LinkedListNode<LodConfig> newNode)
{
base.AddAfter(node, newNode);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value);
}
public new void Remove(LodConfig item)
{
base.Remove(item);
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);
}
public new void RemoveFirst()
{
if (First == null)
return;
base.RemoveFirst();
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, First.Value);
}
public new void RemoveLast()
{
if (Last == null)
return;
base.RemoveLast();
OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, Last.Value);
}
}
}

View file

@ -0,0 +1,21 @@
using MSFSPopoutPanelManager.Shared;
namespace MSFSPopoutPanelManager.DomainModel.Profile
{
public class FixedCameraConfig : ObservableObject
{
public int Id { get; set; } = 0;
public CameraType CameraType { get; set; } = CameraType.Cockpit;
public int CameraIndex { get; set; } = 1;
public string Name { get; set; } = "Cockpit Pilot";
}
public enum CameraType
{
Cockpit = 1,
Instrument = 2
}
}

View file

@ -4,15 +4,9 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public class HudBarConfig : ObservableObject public class HudBarConfig : ObservableObject
{ {
public HudBarConfig() public bool IsEnabled { get; set; } = false;
{
IsEnabled = false;
HudBarType = HudBarType.Generic_Aircraft;
}
public bool IsEnabled { get; set; } public HudBarType HudBarType { get; set; } = HudBarType.Generic_Aircraft;
public HudBarType HudBarType { get; set; }
} }
public enum HudBarType public enum HudBarType

View file

@ -6,21 +6,19 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public MonitorInfo() public MonitorInfo()
{ {
IsSelected = false;
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public string Name { get; set; } public string Name { get; set; }
public bool IsSelected { get; set; } public bool IsSelected { get; set; } = false;
public int X { get; set; } public int X { get; set; } = 0;
public int Y { get; set; } public int Y { get; set; } = 0;
public int Width { get; set; } public int Width { get; set; } = 0;
public int Height { get; set; } public int Height { get; set; } = 0;
} }
} }

View file

@ -0,0 +1,9 @@
using MSFSPopoutPanelManager.Shared;
namespace MSFSPopoutPanelManager.DomainModel.Profile
{
public class NumPadConfig : ObservableObject
{
public bool IsEnabled { get; set; } = false;
}
}

View file

@ -11,11 +11,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
if (Id == Guid.Empty) if (Id == Guid.Empty)
Id = Guid.NewGuid(); Id = Guid.NewGuid();
PanelName = "Default Panel Name";
PanelHandle = IntPtr.MaxValue;
AutoGameRefocus = true;
PanelSource = new PanelSource();
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
PropertyChanged += PanelConfig_PropertyChanged; PropertyChanged += PanelConfig_PropertyChanged;
@ -38,7 +33,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
public PanelType PanelType { get; set; } public PanelType PanelType { get; set; }
public string PanelName { get; set; } public string PanelName { get; set; } = "Default Panel Name";
public int Top { get; set; } public int Top { get; set; }
@ -56,21 +51,18 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
public bool TouchEnabled { get; set; } public bool TouchEnabled { get; set; }
public bool AutoGameRefocus { get; set; } public bool AutoGameRefocus { get; set; } = true;
public PanelSource PanelSource { get; set; } public PanelSource PanelSource { get; set; } = new();
public FixedCameraConfig FixedCameraConfig { get; set; } = new();
[JsonIgnore] [JsonIgnore]
public IntPtr PanelHandle { get; set; } public IntPtr PanelHandle { get; set; } = IntPtr.MaxValue;
[JsonIgnore] [JsonIgnore]
public bool IsEditingPanel { get; set; } public bool IsEditingPanel { get; set; }
[JsonIgnore]
public bool IsCustomPopOut => PanelType == PanelType.CustomPopout;
[JsonIgnore]
public bool IsBuiltInPopOut => PanelType == PanelType.BuiltInPopout;
[JsonIgnore] [JsonIgnore]
public bool HasPanelSource => PanelType == PanelType.BuiltInPopout || (PanelType == PanelType.CustomPopout && PanelSource != null && PanelSource.X != null); public bool HasPanelSource => PanelType == PanelType.BuiltInPopout || (PanelType == PanelType.CustomPopout && PanelSource != null && PanelSource.X != null);
@ -83,10 +75,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
if (PanelHandle == IntPtr.MaxValue) if (PanelHandle == IntPtr.MaxValue)
return null; return null;
if (PanelHandle == IntPtr.Zero) return PanelHandle != IntPtr.Zero;
return false;
return true;
} }
} }
@ -95,5 +84,41 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
[JsonIgnore] [JsonIgnore]
public bool IsShownPanelSource { get; set; } public bool IsShownPanelSource { get; set; }
[JsonIgnore]
public bool IsDeletablePanel => PanelType != PanelType.HudBarWindow && PanelType != PanelType.RefocusDisplay &&
PanelType != PanelType.NumPadWindow;
[JsonIgnore]
public bool IsTouchEnablePanel => PanelType != PanelType.HudBarWindow && PanelType != PanelType.RefocusDisplay &&
PanelType != PanelType.NumPadWindow;
[JsonIgnore]
public bool IsCustomPopOut => PanelType == PanelType.CustomPopout;
[JsonIgnore]
public bool IsBuiltInPopOut => PanelType == PanelType.BuiltInPopout;
[JsonIgnore]
public bool IsHudBarWindow => PanelType == PanelType.HudBarWindow;
[JsonIgnore]
public bool IsRefocusDisplay => PanelType == PanelType.RefocusDisplay;
[JsonIgnore]
public bool IsNumPadWindow => PanelType == PanelType.NumPadWindow;
[JsonIgnore]
public string PanelSourceCoordinateText
{
get
{
if (PanelSource == null || PanelSource.X == null || PanelSource.Y == null)
return "top: N/A, left: N/A";
return $"Left: {PanelSource.X} / Top: {PanelSource.Y}";
}
}
} }
} }

View file

@ -8,7 +8,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public static string GetNextAvailableColor(List<PanelConfig> panelConfigs) public static string GetNextAvailableColor(List<PanelConfig> panelConfigs)
{ {
foreach (string colorName in Enum.GetNames<Colors>()) foreach (var colorName in Enum.GetNames<Colors>())
{ {
if (panelConfigs.Any(p => p.PanelSource.Color == colorName)) if (panelConfigs.Any(p => p.PanelSource.Color == colorName))
continue; continue;

View file

@ -4,12 +4,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public class PanelConfigItem public class PanelConfigItem
{ {
public PanelConfigItem() public IntPtr PanelHandle { get; set; } = IntPtr.Zero;
{
PanelHandle = IntPtr.Zero;
}
public IntPtr PanelHandle { get; set; }
public PanelConfigPropertyName PanelConfigProperty { get; set; } public PanelConfigPropertyName PanelConfigProperty { get; set; }
} }

View file

@ -11,6 +11,7 @@
PanelSourceWindow = 6, PanelSourceWindow = 6,
StatusMessageWindow = 7, StatusMessageWindow = 7,
RefocusDisplay = 8, RefocusDisplay = 8,
NumPadWindow = 9,
Unknown = 100 Unknown = 100
} }
} }

View file

@ -6,9 +6,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public ProfileSetting() public ProfileSetting()
{ {
HudBarConfig = new HudBarConfig();
RefocusOnDisplay = new RefocusOnDisplay();
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
@ -16,8 +13,10 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
public bool IncludeInGamePanels { get; set; } public bool IncludeInGamePanels { get; set; }
public HudBarConfig HudBarConfig { get; set; } public HudBarConfig HudBarConfig { get; set; } = new();
public RefocusOnDisplay RefocusOnDisplay { get; set; } public RefocusOnDisplay RefocusOnDisplay { get; set; } = new();
public NumPadConfig NumPadConfig { get; set; } = new();
} }
} }

View file

@ -8,7 +8,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public RefocusOnDisplay() public RefocusOnDisplay()
{ {
IsEnabled = false;
Monitors = new ObservableCollection<MonitorInfo>(); Monitors = new ObservableCollection<MonitorInfo>();
Monitors.CollectionChanged += (sender, e) => Monitors.CollectionChanged += (sender, e) =>
@ -16,14 +15,13 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
switch (e.Action) switch (e.Action)
{ {
case System.Collections.Specialized.NotifyCollectionChangedAction.Add: case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
if (e.NewItems[0] == null) if (e.NewItems?[0] == null)
return; return;
((MonitorInfo)e.NewItems[0]).PropertyChanged += (sender, e) => ((MonitorInfo)e.NewItems[0]).PropertyChanged += (innerSender, innerArg) =>
{ {
var evtArg = e as PropertyChangedExtendedEventArgs; if (innerArg is PropertyChangedExtendedEventArgs { DisableSave: false })
if (!evtArg.DisableSave) base.OnPropertyChanged(innerSender, innerArg);
base.OnPropertyChanged(sender, e);
}; };
base.OnPropertyChanged(sender, new PropertyChangedEventArgs("Monitors")); base.OnPropertyChanged(sender, new PropertyChangedEventArgs("Monitors"));
break; break;
@ -39,7 +37,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; } = false;
public ObservableCollection<MonitorInfo> Monitors { get; set; } public ObservableCollection<MonitorInfo> Monitors { get; set; }

View file

@ -12,46 +12,38 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
{ {
public UserProfile() public UserProfile()
{ {
Id = Guid.NewGuid(); this.PropertyChanged += (_, e) =>
IsLocked = false;
AircraftBindings = new ObservableCollection<string>();
PanelConfigs = new ObservableCollection<PanelConfig>();
ProfileSetting = new ProfileSetting();
MsfsGameWindowConfig = new MsfsGameWindowConfig();
PanelSourceCockpitZoomFactor = 50;
this.PropertyChanged += (sender, e) =>
{ {
var evtArg = e as PropertyChangedExtendedEventArgs; if (e is PropertyChangedExtendedEventArgs { DisableSave: false })
if (!evtArg.DisableSave) OnProfileChanged?.Invoke(this, EventArgs.Empty);
ProfileChanged?.Invoke(this, null);
if (e.PropertyName == nameof(IsUsedLegacyCameraSystem))
OnPanelConfigChanged();
}; };
PanelConfigs.CollectionChanged += (sender, e) => PanelConfigs.CollectionChanged += (_, e) =>
{ {
switch (e.Action) switch (e.Action)
{ {
case System.Collections.Specialized.NotifyCollectionChangedAction.Add: case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
if (e.NewItems[0] == null) if (e.NewItems?[0] == null)
return; return;
((PanelConfig)e.NewItems[0]).PropertyChanged += (sender, e) => ((PanelConfig)e.NewItems[0]).PropertyChanged += (_, arg) =>
{ {
var evtArg = e as PropertyChangedExtendedEventArgs; if (arg is PropertyChangedExtendedEventArgs { DisableSave: false })
if (!evtArg.DisableSave) OnProfileChanged?.Invoke(this, EventArgs.Empty);
ProfileChanged?.Invoke(this, null);
OnPanelConfigChanged(sender, e); OnPanelConfigChanged();
}; };
ProfileChanged?.Invoke(this, null); OnProfileChanged?.Invoke(this, EventArgs.Empty);
OnPanelConfigChanged(sender, e); OnPanelConfigChanged();
break; break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
case System.Collections.Specialized.NotifyCollectionChangedAction.Move: case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
ProfileChanged?.Invoke(this, null); OnProfileChanged?.Invoke(this, EventArgs.Empty);
OnPanelConfigChanged(sender, e); OnPanelConfigChanged();
break; break;
} }
}; };
@ -59,30 +51,29 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public event EventHandler ProfileChanged; public event EventHandler OnProfileChanged;
public Guid Id { get; set; } public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } public string Name { get; set; }
public bool IsLocked { get; set; } public bool IsLocked { get; set; } = false;
public ObservableCollection<string> AircraftBindings { get; set; } public ObservableCollection<string> AircraftBindings { get; set; } = new();
public ObservableCollection<PanelConfig> PanelConfigs { get; set; } public ObservableCollection<PanelConfig> PanelConfigs { get; set; } = new();
public ProfileSetting ProfileSetting { get; set; } public ProfileSetting ProfileSetting { get; set; } = new();
public MsfsGameWindowConfig MsfsGameWindowConfig { get; set; } public MsfsGameWindowConfig MsfsGameWindowConfig { get; set; } = new();
public int PanelSourceCockpitZoomFactor { get; set; } public int PanelSourceCockpitZoomFactor { get; set; } = 50;
public bool IsUsedLegacyCameraSystem { get; set; } = true;
public int CompareTo(UserProfile other) public int CompareTo(UserProfile other)
{ {
int result = this.Name.ToLower().CompareTo(other.Name.ToLower()); return string.Compare(Name.ToLower(), other.Name.ToLower(), StringComparison.Ordinal);
if (result == 0)
result = this.Name.ToLower().CompareTo(other.Name.ToLower());
return result;
} }
[JsonIgnore] [JsonIgnore]
@ -91,20 +82,23 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
[JsonIgnore] [JsonIgnore]
public bool IsEditingPanelSource { get; set; } public bool IsEditingPanelSource { get; set; }
[JsonIgnore]
public bool IsSelectingPanelSource { get; set; }
private bool _isPoppedOut; private bool _isPoppedOut;
[JsonIgnore] [JsonIgnore]
public bool IsPoppedOut public bool IsPoppedOut
{ {
get { return _isPoppedOut; } get => _isPoppedOut;
set set
{ {
_isPoppedOut = value; _isPoppedOut = value;
if (!value) if (value)
{ return;
foreach (var panelConfig in PanelConfigs)
panelConfig.PanelHandle = IntPtr.MaxValue; // reset panel is popped out status foreach (var panelConfig in PanelConfigs)
} panelConfig.PanelHandle = IntPtr.MaxValue; // reset panel is popped out status
} }
} }
@ -115,12 +109,12 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
public bool HasUnidentifiedPanelSource { get; private set; } public bool HasUnidentifiedPanelSource { get; private set; }
[JsonIgnore] [JsonIgnore]
public bool HasAircraftBindings => AircraftBindings != null && AircraftBindings.Count > 0; public bool IsDisabledStartPopOut { get; set; }
[JsonIgnore] [JsonIgnore]
public bool HasCustomPanels => PanelConfigs.Count(p => p.PanelType == PanelType.CustomPopout) > 0; public bool HasCustomPanels => PanelConfigs != null && PanelConfigs.Count(p => p.PanelType == PanelType.CustomPopout) > 0;
private void OnPanelConfigChanged(object sender, EventArgs e) private void OnPanelConfigChanged()
{ {
HasUnidentifiedPanelSource = PanelConfigs.Any(p => p.PanelType == PanelType.CustomPopout && p.PanelSource.X == null); HasUnidentifiedPanelSource = PanelConfigs.Any(p => p.PanelType == PanelType.CustomPopout && p.PanelSource.X == null);
} }

View file

@ -5,23 +5,15 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class AfterPopOutCameraView : ObservableObject public class AfterPopOutCameraView : ObservableObject
{ {
public AfterPopOutCameraView() public bool IsEnabled { get; set; } = true;
{
// Default values
IsEnabled = true;
CameraView = AfterPopOutCameraViewType.CockpitCenterView;
KeyBinding = "1";
}
public bool IsEnabled { get; set; } public AfterPopOutCameraViewType CameraView { get; set; } = AfterPopOutCameraViewType.CockpitCenterView;
public AfterPopOutCameraViewType CameraView { get; set; } public string KeyBinding { get; set; } = "1";
public string KeyBinding { get; set; }
// Use for MVVM binding only // Use for MVVM binding only
[JsonIgnore] [JsonIgnore]
public bool IsEnabledCustomCameraKeyBinding { get { return IsEnabled && CameraView == AfterPopOutCameraViewType.CustomCameraView; } } public bool IsEnabledCustomCameraKeyBinding => IsEnabled && CameraView == AfterPopOutCameraViewType.CustomCameraView;
} }
public enum AfterPopOutCameraViewType public enum AfterPopOutCameraViewType

View file

@ -14,7 +14,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var filePath = GetFilePath(); var filePath = GetFilePath();
var autoStartArg = new LaunchAddOn() { Name = "MSFS Popout Panel Manager", Disabled = "false", Path = $@"{Directory.GetCurrentDirectory()}\MSFSPopoutPanelManager.exe" }; var autoStartArg = new LaunchAddOn() { Name = "MSFS Popout Panel Manager", Disabled = "false", Path = $@"{Directory.GetCurrentDirectory()}\MSFSPopoutPanelManager.exe" };
XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument)); var serializer = new XmlSerializer(typeof(SimBaseDocument));
if (filePath != null && File.Exists(filePath)) if (filePath != null && File.Exists(filePath))
{ {
@ -26,32 +26,33 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
SimBaseDocument data; SimBaseDocument data;
using (Stream stream = new FileStream(filePath, FileMode.Open)) using (var stream = new FileStream(filePath, FileMode.Open))
{ {
data = (SimBaseDocument)serializer.Deserialize(stream); data = (SimBaseDocument)serializer.Deserialize(stream);
} }
if (data != null) if (data == null)
return;
if (data.LaunchAddOn.Count == 0)
{ {
if (data.LaunchAddOn.Count == 0) data.LaunchAddOn.Add(autoStartArg);
{ }
data.LaunchAddOn.Add(autoStartArg); else
} {
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
if (autoStartIndex > -1)
data.LaunchAddOn[autoStartIndex] = autoStartArg;
else else
{ data.LaunchAddOn.Add(autoStartArg);
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager"); }
if (autoStartIndex > -1) using (var stream = new FileStream(filePath, FileMode.Open))
data.LaunchAddOn[autoStartIndex] = autoStartArg; {
else stream.SetLength(0);
data.LaunchAddOn.Add(autoStartArg); serializer.Serialize(stream, data,
} new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
using (Stream stream = new FileStream(filePath, FileMode.Open))
{
stream.SetLength(0);
serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
}
} }
} }
else else
@ -61,15 +62,16 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
Descr = "SimConnect", Descr = "SimConnect",
Filename = "SimConnect.xml", Filename = "SimConnect.xml",
Disabled = "False", Disabled = "False",
Type = "SimConnecct", Type = "SimConnect",
Version = "1,0", Version = "1,0",
LaunchAddOn = new List<LaunchAddOn>() { autoStartArg } LaunchAddOn = new List<LaunchAddOn>() { autoStartArg }
}; };
using (var file = File.Create(filePath)) if (filePath == null)
{ return;
serializer.Serialize(file, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
} using var file = File.Create(filePath);
serializer.Serialize(file, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
} }
} }
@ -77,32 +79,32 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
var filePath = GetFilePath(); var filePath = GetFilePath();
if (filePath != null && File.Exists(filePath)) if (filePath == null || !File.Exists(filePath))
return;
SimBaseDocument data;
var serializer = new XmlSerializer(typeof(SimBaseDocument));
using (var stream = new FileStream(filePath, FileMode.Open))
{ {
SimBaseDocument data; data = (SimBaseDocument)serializer.Deserialize(stream);
XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument)); }
using (Stream stream = new FileStream(filePath, FileMode.Open)) if (data == null)
{ return;
data = (SimBaseDocument)serializer.Deserialize(stream);
}
if (data != null) if (data.LaunchAddOn.Count > 0)
{ {
if (data.LaunchAddOn.Count > 0) var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
{
var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager");
if (autoStartIndex > -1) if (autoStartIndex > -1)
data.LaunchAddOn.RemoveAt(autoStartIndex); data.LaunchAddOn.RemoveAt(autoStartIndex);
} }
using (Stream stream = new FileStream(filePath, FileMode.Open)) using (var stream = new FileStream(filePath, FileMode.Open))
{ {
stream.SetLength(0); stream.SetLength(0);
serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
}
}
} }
} }
@ -141,11 +143,11 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
private static string GetFilePath() private static string GetFilePath()
{ {
var filePathMSStore = Environment.ExpandEnvironmentVariables("%LocalAppData%") + @"\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache\"; var filePathMsStore = Environment.ExpandEnvironmentVariables("%LocalAppData%") + @"\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache\";
var filePathSteam = Environment.ExpandEnvironmentVariables("%AppData%") + @"\Microsoft Flight Simulator\LocalCache\"; var filePathSteam = Environment.ExpandEnvironmentVariables("%AppData%") + @"\Microsoft Flight Simulator\LocalCache\";
if (Directory.Exists(filePathMSStore)) if (Directory.Exists(filePathMsStore))
return filePathMSStore + "exe.xml"; return filePathMsStore + "exe.xml";
else if (Directory.Exists(filePathSteam)) else if (Directory.Exists(filePathSteam))
return filePathSteam + "exe.xml"; return filePathSteam + "exe.xml";
else else

View file

@ -7,45 +7,35 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public ApplicationSetting() public ApplicationSetting()
{ {
GeneralSetting = new GeneralSetting();
AutoPopOutSetting = new AutoPopOutSetting();
PopOutSetting = new PopOutSetting();
RefocusSetting = new RefocusSetting();
TouchSetting = new TouchSetting();
TrackIRSetting = new TrackIRSetting();
WindowedModeSetting = new WindowedModeSetting();
SystemSetting = new SystemSetting();
KeyboardShortcutSetting = new KeyboardShortcutSetting();
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
this.PropertyChanged += (sender, e) => PropertyChanged += (_, e) =>
{ {
var evtArg = e as PropertyChangedExtendedEventArgs; if (e is PropertyChangedExtendedEventArgs { ObjectName: "MSFSPopoutPanelManager.DomainModel.Setting.KeyboardShortcutSetting", PropertyName: "IsEnabled" })
OnIsUsedKeyboardShortcutChanged?.Invoke(this, KeyboardShortcutSetting.IsEnabled);
if (evtArg.ObjectName == "MSFSPopoutPanelManager.DomainModel.Setting.KeyboardShortcutSetting" && evtArg.PropertyName == "IsEnabled")
IsUsedKeyboardShortcutChanged?.Invoke(this, KeyboardShortcutSetting.IsEnabled);
}; };
} }
public GeneralSetting GeneralSetting { get; set; } public GeneralSetting GeneralSetting { get; set; } = new();
public AutoPopOutSetting AutoPopOutSetting { get; set; } public AutoPopOutSetting AutoPopOutSetting { get; set; } = new();
public PopOutSetting PopOutSetting { get; set; } public PopOutSetting PopOutSetting { get; set; } = new();
public RefocusSetting RefocusSetting { get; set; } public RefocusSetting RefocusSetting { get; set; } = new();
public TouchSetting TouchSetting { get; set; } public TouchSetting TouchSetting { get; set; } = new();
public TrackIRSetting TrackIRSetting { get; set; } public TrackIRSetting TrackIRSetting { get; set; } = new();
public WindowedModeSetting WindowedModeSetting { get; set; } public WindowedModeSetting WindowedModeSetting { get; set; } = new();
public SystemSetting SystemSetting { get; set; } public SystemSetting SystemSetting { get; set; } = new();
public KeyboardShortcutSetting KeyboardShortcutSetting { get; set; } public KeyboardShortcutSetting KeyboardShortcutSetting { get; set; } = new();
public event EventHandler<bool> IsUsedKeyboardShortcutChanged; public DynamicLodSetting DynamicLodSetting { get; set; } = new();
public event EventHandler<bool> OnIsUsedKeyboardShortcutChanged;
} }
} }

View file

@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class AutoPanning : ObservableObject public class AutoPanning : ObservableObject
{ {
public AutoPanning() public bool IsEnabled { get; set; } = true;
{
IsEnabled = true;
KeyBinding = "0";
}
public bool IsEnabled { get; set; } public string KeyBinding { get; set; } = "0";
public string KeyBinding { get; set; }
} }
} }

View file

@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class AutoPopOutSetting : ObservableObject public class AutoPopOutSetting : ObservableObject
{ {
public AutoPopOutSetting() public bool IsEnabled { get; set; } = true;
{
IsEnabled = true;
ReadyToFlyDelay = 3;
}
public bool IsEnabled { get; set; } public int ReadyToFlyDelay { get; set; } = 3;
public int ReadyToFlyDelay { get; set; }
} }
} }

View file

@ -0,0 +1,71 @@
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.Shared;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.Serialization;
namespace MSFSPopoutPanelManager.DomainModel.Setting
{
public class DynamicLodSetting : ObservableObject
{
public DynamicLodSetting()
{
InitializeChildPropertyChangeBinding();
TlodConfigs.CollectionChanged += (_, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
if (e.NewItems?[0] is LodConfig item)
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
}
};
OlodConfigs.CollectionChanged += (_, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
if (e.NewItems?[0] is LodConfig item)
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
}
};
}
public bool IsEnabled { get; set; } = false;
public ObservableLodConfigLinkedList TlodConfigs { get; set; } = new();
public ObservableLodConfigLinkedList OlodConfigs { get; set; } = new();
public bool ResetEnabled { get; set; } = false;
public int ResetTlod { get; set; } = 100;
public int ResetOlod { get; set; } = 100;
public void AddDefaultTLodConfigs()
{
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 100 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 200 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 10000, Lod = 300 }));
TlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 20000, Lod = 400 }));
}
public void AddDefaultOLodConfigs()
{
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 0, Lod = 200 }));
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 1000, Lod = 150 }));
OlodConfigs.AddLast(new LinkedListNode<LodConfig>(new LodConfig { Agl = 5000, Lod = 100 }));
}
[OnDeserialized]
private void OnDeserialization(StreamingContext context)
{
foreach (var item in TlodConfigs)
item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
foreach (var item in OlodConfigs)
item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item);
}
}
}

View file

@ -7,35 +7,25 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public GeneralSetting() public GeneralSetting()
{ {
AlwaysOnTop = true;
MinimizeToTray = false;
StartMinimized = false;
AutoClose = true;
CheckForUpdate = true;
TurboMode = false;
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public bool AlwaysOnTop { get; set; } public bool AlwaysOnTop { get; set; } = true;
public bool AutoClose { get; set; } public bool AutoClose { get; set; } = true;
public bool MinimizeToTray { get; set; } public bool MinimizeToTray { get; set; } = false;
public bool StartMinimized { get; set; } public bool StartMinimized { get; set; } = false;
public bool CheckForUpdate { get; set; } public bool CheckForUpdate { get; set; } = true;
public bool TurboMode { get; set; } public bool TurboMode { get; set; } = false;
[JsonIgnore, IgnorePropertyChanged] [JsonIgnore, IgnorePropertyChanged]
public bool AutoStart public bool AutoStart
{ {
get get => AppAutoStart.CheckIsAutoStart();
{
return AppAutoStart.CheckIsAutoStart();
}
set set
{ {
if (value) if (value)

View file

@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class KeyboardShortcutSetting : ObservableObject public class KeyboardShortcutSetting : ObservableObject
{ {
public KeyboardShortcutSetting() public bool IsEnabled { get; set; } = true;
{
IsEnabled = true;
StartPopOutKeyBinding = "O";
}
public bool IsEnabled { get; set; } public string StartPopOutKeyBinding { get; set; } = "O";
public string StartPopOutKeyBinding { get; set; }
} }
} }

View file

@ -6,36 +6,25 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public PopOutSetting() public PopOutSetting()
{ {
MinimizeDuringPopOut = true;
MinimizeAfterPopOut = false;
UseLeftRightControlToPopOut = false;
AutoActivePause = false;
EnablePopOutMessages = true;
AfterPopOutCameraView = new AfterPopOutCameraView();
AutoPanning = new AutoPanning();
PopOutTitleBarCustomization = new PopOutTitleBarCustomization();
EnablePanelResetWhenLocked = true;
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public AutoPanning AutoPanning { get; set; } public AutoPanning AutoPanning { get; set; } = new();
public AfterPopOutCameraView AfterPopOutCameraView { get; set; } public AfterPopOutCameraView AfterPopOutCameraView { get; set; } = new();
public bool MinimizeDuringPopOut { get; set; } public bool MinimizeDuringPopOut { get; set; } = true;
public bool MinimizeAfterPopOut { get; set; } public bool MinimizeAfterPopOut { get; set; } = false;
public bool UseLeftRightControlToPopOut { get; set; } public bool UseLeftRightControlToPopOut { get; set; } = false;
public bool EnablePanelResetWhenLocked { get; set; } public bool EnablePanelResetWhenLocked { get; set; } = true;
public bool EnablePopOutMessages { get; set; } public bool EnablePopOutMessages { get; set; } = true;
public bool AutoActivePause { get; set; } public bool AutoActivePause { get; set; } = false;
public PopOutTitleBarCustomization PopOutTitleBarCustomization { get; set; } public PopOutTitleBarCustomization PopOutTitleBarCustomization { get; set; } = new();
}; };
} }

View file

@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class PopOutTitleBarCustomization : ObservableObject public class PopOutTitleBarCustomization : ObservableObject
{ {
public PopOutTitleBarCustomization() public bool IsEnabled { get; set; } = false;
{
IsEnabled = false;
HexColor = "000000";
}
public bool IsEnabled { get; set; } public string HexColor { get; set; } = "000000";
public string HexColor { get; set; }
} }
} }

View file

@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class RefocusGameWindow : ObservableObject public class RefocusGameWindow : ObservableObject
{ {
public RefocusGameWindow() public bool IsEnabled { get; set; } = true;
{
IsEnabled = true;
Delay = 1;
}
public bool IsEnabled { get; set; } public double Delay { get; set; } = 1;
public double Delay { get; set; }
} }
} }

View file

@ -6,11 +6,9 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public RefocusSetting() public RefocusSetting()
{ {
RefocusGameWindow = new RefocusGameWindow();
InitializeChildPropertyChangeBinding(); InitializeChildPropertyChangeBinding();
} }
public RefocusGameWindow RefocusGameWindow { get; set; } public RefocusGameWindow RefocusGameWindow { get; set; } = new();
} }
} }

View file

@ -5,14 +5,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class SystemSetting : ObservableObject public class SystemSetting : ObservableObject
{ {
public SystemSetting() public string AutoUpdaterUrl { get; set; } = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml";
{
LastUsedProfileId = Guid.Empty;
AutoUpdaterUrl = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml";
}
public string AutoUpdaterUrl { get; set; } public Guid LastUsedProfileId { get; set; } = Guid.Empty;
public Guid LastUsedProfileId { get; set; }
} }
} }

View file

@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class TouchSetting : ObservableObject public class TouchSetting : ObservableObject
{ {
public TouchSetting() public int TouchDownUpDelay { get; set; } = 0;
{
TouchDownUpDelay = 0;
}
public int TouchDownUpDelay { get; set; }
} }
} }

View file

@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class TrackIRSetting : ObservableObject public class TrackIRSetting : ObservableObject
{ {
public TrackIRSetting() public bool AutoDisableTrackIR { get; set; } = false;
{
AutoDisableTrackIR = true;
}
public bool AutoDisableTrackIR { get; set; }
} }
} }

View file

@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting
{ {
public class WindowedModeSetting : ObservableObject public class WindowedModeSetting : ObservableObject
{ {
public WindowedModeSetting() public bool AutoResizeMsfsGameWindow { get; set; } = true;
{
AutoResizeMsfsGameWindow = true;
}
public bool AutoResizeMsfsGameWindow { get; set; }
} }
} }

View file

@ -1,7 +1,6 @@
using MSFSPopoutPanelManager.DomainModel.Profile; using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.Reflection;
namespace MSFSPopoutPanelManager.DomainModel.SimConnect namespace MSFSPopoutPanelManager.DomainModel.SimConnect
{ {
@ -72,8 +71,9 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect
private string MapGear(double gear) private string MapGear(double gear)
{ {
if (gear == 100) if (Convert.ToInt32(gear) == 100)
return "DOWN"; return "DOWN";
if (gear == 0) if (gear == 0)
return "UP"; return "UP";
@ -82,12 +82,12 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect
public void Clear() public void Clear()
{ {
Type type = this.GetType(); var type = this.GetType();
PropertyInfo[] properties = type.GetProperties(); var properties = type.GetProperties();
for (int i = 0; i < properties.Length; ++i) foreach (var property in properties)
{ {
if (properties[i].SetMethod != null) if (property.SetMethod != null)
properties[i].SetValue(this, 0); property.SetValue(this, 0);
} }
} }
} }

View file

@ -1,7 +1,6 @@
using MSFSPopoutPanelManager.DomainModel.Profile; using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.Reflection;
namespace MSFSPopoutPanelManager.DomainModel.SimConnect namespace MSFSPopoutPanelManager.DomainModel.SimConnect
{ {
@ -45,8 +44,9 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect
private string MapGear(double gear) private string MapGear(double gear)
{ {
if (gear == 100) if (Convert.ToInt32(gear) == 100)
return "DOWN"; return "DOWN";
if (gear == 0) if (gear == 0)
return "UP"; return "UP";
@ -55,12 +55,12 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect
public void Clear() public void Clear()
{ {
Type type = this.GetType(); var type = this.GetType();
PropertyInfo[] properties = type.GetProperties(); var properties = type.GetProperties();
for (int i = 0; i < properties.Length; ++i) foreach (var property in properties)
{ {
if (properties[i].SetMethod != null) if (property.SetMethod != null)
properties[i].SetValue(this, 0); property.SetValue(this, 0);
} }
} }
} }

View file

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using MSFSPopoutPanelManager.MainApp.AppWindow;
using MSFSPopoutPanelManager.MainApp.ViewModel; using MSFSPopoutPanelManager.MainApp.ViewModel;
using MSFSPopoutPanelManager.Orchestration; using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
@ -9,20 +10,18 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using Application = System.Windows.Application;
namespace MSFSPopoutPanelManager.MainApp namespace MSFSPopoutPanelManager.MainApp
{ {
public partial class App : Application public partial class App
{ {
public static IHost AppHost { get; private set; } public SharedStorage SharedStorage;
public App() public static IHost AppHost { get; private set; }
{
}
protected override async void OnStartup(StartupEventArgs e) protected override async void OnStartup(StartupEventArgs e)
{ {
@ -36,45 +35,60 @@ namespace MSFSPopoutPanelManager.MainApp
} }
else else
{ {
// Setup all unhandle exception handlers // Setup all unhandled exception handlers
Dispatcher.UnhandledException += HandleDispatcherException; Dispatcher.UnhandledException += HandleDispatcherException;
TaskScheduler.UnobservedTaskException += HandleTaskSchedulerUnobservedTaskException; TaskScheduler.UnobservedTaskException += HandleTaskSchedulerUnobservedTaskException;
AppDomain.CurrentDomain.UnhandledException += HandledDomainException; AppDomain.CurrentDomain.UnhandledException += HandledDomainException;
// Setup all data storage objects
SharedStorage = new SharedStorage();
SharedStorage.AppSettingData.ReadSettings();
SharedStorage.ProfileData.ReadProfiles();
// Setup dependency injections // Setup dependency injections
AppHost = Host.CreateDefaultBuilder() AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) => .ConfigureServices((_, services) =>
{ {
services.AddSingleton<AppWindow>(); services.AddSingleton<AppMainWindow>();
services.AddSingleton<MainOrchestrator>(); services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<HelpOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
services.AddSingleton<OrchestratorUIHelper>(s => new OrchestratorUIHelper(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new ProfileOrchestrator(SharedStorage));
services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage));
services.AddSingleton(s => new KeyboardOrchestrator(SharedStorage, s.GetRequiredService<PanelPopOutOrchestrator>()));
services.AddSingleton(s => new HelpOrchestrator());
services.AddSingleton<ApplicationViewModel>(s => new ApplicationViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new OrchestratorUiHelper(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
services.AddSingleton<HelpViewModel>(s => new HelpViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new ApplicationViewModel(SharedStorage, s.GetRequiredService<AppOrchestrator>()));
services.AddSingleton<ProfileCardListViewModel>(s => new ProfileCardListViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new HelpViewModel(SharedStorage, s.GetRequiredService<HelpOrchestrator>()));
services.AddSingleton<ProfileCardViewModel>(s => new ProfileCardViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new ProfileCardListViewModel(SharedStorage, s.GetRequiredService<ProfileOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>()));
services.AddSingleton<TrayIconViewModel>(s => new TrayIconViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddSingleton(s => new ProfileCardViewModel(SharedStorage, s.GetRequiredService<ProfileOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
services.AddSingleton(s => new TrayIconViewModel(SharedStorage, s.GetRequiredService<AppOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
services.AddTransient<AddProfileViewModel>(s => new AddProfileViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new AddProfileViewModel(SharedStorage, s.GetRequiredService<ProfileOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>()));
services.AddTransient<PopOutPanelListViewModel>(s => new PopOutPanelListViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new PopOutPanelListViewModel(SharedStorage));
services.AddTransient<PopOutPanelCardViewModel>(s => new PopOutPanelCardViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new PopOutPanelConfigCardViewModel(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
services.AddTransient<PanelConfigFieldViewModel>(s => new PanelConfigFieldViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new PopOutPanelSourceCardViewModel(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
services.AddTransient<PanelCoorOverlayViewModel>(s => new PanelCoorOverlayViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new PopOutPanelSourceLegacyCardViewModel(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
services.AddTransient(s => new PanelConfigFieldViewModel(SharedStorage, s.GetRequiredService<PanelConfigurationOrchestrator>()));
services.AddTransient(s => new PanelCoorOverlayViewModel(SharedStorage));
services.AddTransient<MessageWindowViewModel>(s => new MessageWindowViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new MessageWindowViewModel(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
services.AddTransient<HudBarViewModel>(s => new HudBarViewModel(s.GetRequiredService<MainOrchestrator>())); services.AddTransient(s => new HudBarViewModel(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
services.AddTransient(s => new NumPadViewModel(SharedStorage));
}).Build(); }).Build();
await AppHost!.StartAsync(); await AppHost!.StartAsync();
// Startup window (must come after DPI setup above) // Startup window (must come after DPI setup above)
MainWindow = AppHost.Services.GetRequiredService<AppWindow>(); MainWindow = AppHost.Services.GetRequiredService<AppMainWindow>();
MainWindow.Show(); MainWindow?.Show();
// Setup orchestration UI handler // Setup orchestration UI handler
var orchestrationUIHelper = App.AppHost.Services.GetRequiredService<OrchestratorUIHelper>(); App.AppHost.Services.GetRequiredService<OrchestratorUiHelper>();
// Setup message window dialog // Setup message window dialog
var messageWindow = new MessageWindow(); var messageWindow = new MessageWindow();
@ -86,10 +100,19 @@ namespace MSFSPopoutPanelManager.MainApp
private bool IsRunning() private bool IsRunning()
{ {
return Process.GetProcesses().Count(p => p.ProcessName.Contains(Assembly.GetEntryAssembly().GetName().Name)) > 1; var assembly = Assembly.GetEntryAssembly();
if (assembly == null)
return false;
var assemblyName = assembly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
return false;
return Process.GetProcesses().Count(p => p.ProcessName.Contains(assemblyName)) > 1;
} }
private void HandleTaskSchedulerUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) private void HandleTaskSchedulerUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{ {
FileLogger.WriteException(e.Exception.Message, e.Exception); FileLogger.WriteException(e.Exception.Message, e.Exception);
} }
@ -110,18 +133,18 @@ namespace MSFSPopoutPanelManager.MainApp
} }
} }
public enum PROCESS_DPI_AWARENESS public enum ProcessDpiAwareness
{ {
Process_DPI_Unaware = 0, //PROCESS_DPI_UNAWARE = 0,
Process_System_DPI_Aware = 1, //PROCESS_SYSTEM_DPI_AWARE = 1,
Process_Per_Monitor_DPI_Aware = 2 PROCESS_PER_MONITOR_DPI_AWARE = 2
} }
public enum DPI_AWARENESS_CONTEXT public enum DpiAwarenessContext
{ {
DPI_AWARENESS_CONTEXT_UNAWARE = 16, //DPI_AWARENESS_CONTEXT_UNAWARE = 16,
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17, //DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18, //DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
} }
@ -135,11 +158,11 @@ namespace MSFSPopoutPanelManager.MainApp
// Windows 10 creators update added support for per monitor v2 // Windows 10 creators update added support for per monitor v2
if (Environment.OSVersion.Version >= new Version(10, 0, 15063)) if (Environment.OSVersion.Version >= new Version(10, 0, 15063))
{ {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); SetProcessDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
} }
else else
{ {
SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware); SetProcessDpiAwareness(ProcessDpiAwareness.PROCESS_PER_MONITOR_DPI_AWARE);
} }
} }
else else
@ -148,20 +171,19 @@ namespace MSFSPopoutPanelManager.MainApp
} }
var process = WindowProcessManager.GetApplicationProcess(); var process = WindowProcessManager.GetApplicationProcess();
PROCESS_DPI_AWARENESS outValue; GetProcessDpiAwareness(process.Handle, out _);
GetProcessDpiAwareness(process.Handle, out outValue);
} }
[DllImport("User32.dll")] [DllImport("User32.dll")]
internal static extern bool SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiFlag); internal static extern bool SetProcessDpiAwarenessContext(DpiAwarenessContext dpiFlag);
[DllImport("SHCore.dll")] [DllImport("SHCore.dll")]
internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness); internal static extern bool SetProcessDpiAwareness(ProcessDpiAwareness awareness);
[DllImport("User32.dll")] [DllImport("User32.dll")]
internal static extern bool SetProcessDPIAware(); internal static extern bool SetProcessDPIAware();
[DllImport("SHCore.dll", SetLastError = true)] [DllImport("SHCore.dll", SetLastError = true)]
internal static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness); internal static extern void GetProcessDpiAwareness(IntPtr hProcess, out ProcessDpiAwareness awareness);
} }
} }

View file

@ -0,0 +1,92 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.AddProfileDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
Width="400"
Height="210"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
mc:Ignorable="d">
<UserControl.Resources>
<converter:InverseBooleanOrConverter x:Key="InverseBooleanOrConverter" />
</UserControl.Resources>
<StackPanel d:DataContext="{d:DesignInstance viewmodel:AddProfileViewModel}">
<materialDesign:ColorZone
Height="30"
materialDesign:ElevationAssist.Elevation="Dp4"
Mode="PrimaryDark">
<StackPanel
Margin="24,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBlock Text="Add Profile" />
</StackPanel>
</materialDesign:ColorZone>
<StackPanel
Height="84"
Margin="24,12,24,16"
HorizontalAlignment="Stretch"
FocusManager.FocusedElement="{Binding ElementName=TxtBoxName}">
<ComboBox
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="Copy From Profile"
DisplayMemberPath="Name"
ItemsSource="{Binding ProfileData.Profiles}"
MaxDropDownHeight="280"
SelectedValue="{Binding CopiedProfile, Mode=TwoWay}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
<TextBox
x:Name="TxtBoxName"
Margin="0,8,0,0"
VerticalAlignment="Center"
materialDesign:HintAssist.FloatingScale="0.75"
materialDesign:HintAssist.Hint="Profile Name"
Style="{StaticResource MaterialDesignFloatingHintTextBox}">
<Binding
Mode="TwoWay"
Path="Profile.Name"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<viewmodel:ProfileNameValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
</StackPanel>
<StackPanel
Height="50"
Margin="24,12,24,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="BtnAccept"
Command="{x:Static wpf:DialogHost.CloseDialogCommand}"
CommandParameter="ADD"
IsDefault="True"
Style="{StaticResource MaterialDesignOutlinedButton}">
ACCEPT
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource InverseBooleanOrConverter}" Mode="TwoWay">
<Binding ElementName="TxtBoxName" Path="(Validation.HasError)" />
<Binding Path="Profile.Name.Length" />
</MultiBinding>
</Button.IsEnabled>
</Button>
<Button
x:Name="BtnCancel"
Margin="12,0,0,0"
Command="{x:Static wpf:DialogHost.CloseDialogCommand}"
CommandParameter="CANCEL"
IsCancel="True"
Style="{StaticResource MaterialDesignOutlinedButton}">
CANCEL
</Button>
</StackPanel>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,29 @@
using MaterialDesignThemes.Wpf;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System.ComponentModel;
using System.Windows;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog
{
public partial class AddProfileDialog
{
public AddProfileViewModel ViewModel { get; set; }
public AddProfileDialog()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
ViewModel = App.AppHost.Services.GetRequiredService<AddProfileViewModel>();
Loaded += (_, _) =>
{
DataContext = ViewModel;
BtnAccept.IsEnabled = false;
};
}
public DialogClosingEventHandler ClosingEventHandler => ViewModel.ClosingEventHandler;
}
}

View file

@ -0,0 +1,56 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.ConfirmationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
Width="400"
MinHeight="170"
mc:Ignorable="d">
<StackPanel d:DataContext="{d:DesignInstance viewModel:ConfirmationViewModel}">
<materialDesign:ColorZone
Height="30"
materialDesign:ElevationAssist.Elevation="Dp4"
Mode="PrimaryDark">
<StackPanel
Margin="24,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</materialDesign:ColorZone>
<StackPanel
MinHeight="40"
Margin="24,24,24,16"
Orientation="Vertical">
<TextBlock
VerticalAlignment="Top"
Text="{Binding Content}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
Margin="24,12,24,8"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
Command="{x:Static wpf:DialogHost.CloseDialogCommand}"
CommandParameter="CONFIRM"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}">
<TextBlock Text="{Binding ConfirmButtonText}" />
</Button>
<Button
Margin="12,0,0,0"
Command="{x:Static wpf:DialogHost.CloseDialogCommand}"
CommandParameter="CANCEL"
IsCancel="True"
Style="{StaticResource MaterialDesignOutlinedButton}">
CANCEL
</Button>
</StackPanel>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,18 @@
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System.ComponentModel;
using System.Windows;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog
{
public partial class ConfirmationDialog
{
public ConfirmationDialog(string content, string confirmButtonText)
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
DataContext = new ConfirmationViewModel(content, confirmButtonText);
}
}
}

View file

@ -0,0 +1,556 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.DynamicLodPreference"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mainApp="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
d:DesignHeight="800"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">22</system:Double>
<Style
x:Key="TextBlockLabel"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="650" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="LineHeight" Value="20" />
</Style>
<Style
x:Key="ToggleButton"
BasedOn="{StaticResource MaterialDesignSwitchToggleButton}"
TargetType="ToggleButton">
<Setter Property="Margin" Value="4,0,4,0" />
</Style>
</UserControl.Resources>
<Grid d:DataContext="{d:DesignInstance viewModel:ApplicationViewModel}">
<WrapPanel Margin="30,10,0,0">
<!-- Terrain level of detail -->
<WrapPanel Margin="20,0,0,0" Orientation="Vertical">
<TextBlock
Margin="0,0,0,5"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="Bold">
Terrain Level of Detail (TLOD)
</TextBlock>
<DataGrid
Name="TlodGrid"
Width="272"
Height="248"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="1"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
GridLinesVisibility="None"
HeadersVisibility="Column"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
FontSize="14"
Text="{Binding}"
TextAlignment="Center"
TextWrapping="Wrap" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="30" />
<Setter Property="Background" Value="#FF576573" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
HorizontalAlignment="Center"
Click="TLodDelete_Click"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Delete TLOD configuration">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="Delete" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Margin="0,5,0,0">
<DataGrid
Name="AddTlodGrid"
Width="272"
Height="50"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
GridLinesVisibility="None"
HeadersVisibility="None"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AddTlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9"
VerticalScrollBarVisibility="Disabled">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New AGL"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddTlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New LOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddTlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Kind="Add" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</WrapPanel>
</WrapPanel>
<!-- Object level of detail -->
<WrapPanel Margin="50,0,0,0" Orientation="Vertical">
<TextBlock
Margin="0,0,0,5"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="Bold">
Object Level of Detail (OLOD)
</TextBlock>
<DataGrid
Name="OlodGrid"
Width="272"
Height="248"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="1"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
GridLinesVisibility="None"
HeadersVisibility="Column"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
FontSize="14"
Text="{Binding}"
TextAlignment="Center"
TextWrapping="Wrap" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="30" />
<Setter Property="Background" Value="#FF576573" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14">
<TextBox.Text>
<Binding
Mode="TwoWay"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
HorizontalAlignment="Center"
Click="OLodDelete_Click"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Delete OLOD configuration">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="Delete" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Margin="0,5,0,0">
<DataGrid
Name="AddOlodGrid"
Width="272"
Height="50"
Margin="0"
HorizontalAlignment="Center"
AutoGenerateColumns="False"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
GridLinesVisibility="None"
HeadersVisibility="None"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding AddOlodConfigs}"
SelectionUnit="FullRow"
VerticalGridLinesBrush="#B9B9B9"
VerticalScrollBarVisibility="Disabled">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="BorderBrush" Value="#FFB9B9B9" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="100" Header="AGL">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New AGL"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddOlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Agl"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:AglValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="LOD">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox
Width="100"
materialDesign:HintAssist.Hint="New LOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="0"
FontSize="14"
SourceUpdated="AddOlod_SourceUpdated">
<TextBox.Text>
<Binding
Mode="TwoWay"
NotifyOnSourceUpdated="True"
Path="Lod"
UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="70" Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Kind="Add" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,0,0" Orientation="Horizontal">
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable reset of TLOD and OLOD to the following values when flight session ends.
</TextBlock>
<WrapPanel IsEnabled="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled}" Orientation="Horizontal">
<TextBox
Width="100"
Height="40"
Margin="45,5,0,0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.Hint="Reset TLOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="1"
FontSize="14"
Style="{StaticResource MaterialDesignFloatingHintTextBox}">
<TextBox.Text>
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox
Width="100"
Height="40"
Margin="20,5,0,0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.Hint="Reset OLOD"
materialDesign:ValidationAssist.HorizontalAlignment="Center"
BorderThickness="1"
FontSize="14"
Style="{StaticResource MaterialDesignFloatingHintTextBox}">
<TextBox.Text>
<Binding Mode="TwoWay" Path="AppSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod">
<Binding.ValidationRules>
<appUserControl:LodValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
</WrapPanel>
</WrapPanel>
</Grid>
</UserControl>

View file

@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using MSFSPopoutPanelManager.DomainModel.DynamicLod;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class DynamicLodPreference
{
private ObservableLodConfigLinkedList _tlodConfigs;
private ObservableLodConfigLinkedList _olodConfigs;
public DynamicLodPreference()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
InitializeComponent();
return;
}
AddTlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
AddOlodConfigs = new ObservableCollection<TempLodConfig>() { new () };
Loaded += (_, _) =>
{
InitializeComponent();
var dataContext = DataContext as ApplicationViewModel;
_tlodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs;
_olodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs;
};
}
public ObservableCollection<TempLodConfig> AddTlodConfigs { get; set; }
public ObservableCollection<TempLodConfig> AddOlodConfigs { get; set; }
private void AddTlod_SourceUpdated(object sender, DataTransferEventArgs e)
{
var textBox = sender as TextBox;
var lodConfig = textBox?.DataContext as TempLodConfig;
if (lodConfig?.Agl == null || lodConfig.Lod == null)
return;
if (UpdateTlodDuplicate(lodConfig))
{
RebindTLodGrid();
return;
}
var targetLodConfig = _tlodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
var newLodConfig = new LodConfig { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
if (targetLodConfig == null)
_tlodConfigs.AddFirst(newLodConfig);
else
_tlodConfigs.AddAfter(_tlodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
RebindTLodGrid();
}
private void TLodDelete_Click(object sender, RoutedEventArgs e)
{
var button = e.Source as Button;
if(button?.DataContext is not LodConfig lodConfig)
return;
_tlodConfigs.Remove(lodConfig);
RebindTLodGrid();
}
private void RebindTLodGrid()
{
this.TlodGrid.ItemsSource = null;
this.TlodGrid.ItemsSource = _tlodConfigs.ToList();
AddTlodConfigs.Clear();
AddTlodConfigs.Add(new TempLodConfig());
}
private bool UpdateTlodDuplicate(TempLodConfig lodConfig)
{
var tlodConfig = _tlodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
if(tlodConfig == null)
return false;
tlodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
return true;
}
private void AddOlod_SourceUpdated(object sender, DataTransferEventArgs e)
{
var textBox = sender as TextBox;
var lodConfig = textBox?.DataContext as TempLodConfig;
if (lodConfig?.Agl == null || lodConfig.Lod == null)
return;
if (UpdateOlodDuplicate(lodConfig))
{
RebindOLodGrid();
return;
}
var targetLodConfig = _olodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl);
var newLodConfig = new LodConfig() { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod };
if (targetLodConfig == null)
_olodConfigs.AddFirst(newLodConfig);
else
_olodConfigs.AddAfter(_olodConfigs.Find(targetLodConfig), new LinkedListNode<LodConfig>(newLodConfig));
RebindOLodGrid();
}
private void OLodDelete_Click(object sender, RoutedEventArgs e)
{
var button = e.Source as Button;
if (button?.DataContext is not LodConfig lodConfig)
return;
_olodConfigs.Remove(lodConfig);
RebindOLodGrid();
}
private void RebindOLodGrid()
{
this.OlodGrid.ItemsSource = null;
this.OlodGrid.ItemsSource = _olodConfigs.ToList();
AddOlodConfigs.Clear();
AddOlodConfigs.Add(new TempLodConfig());
}
private bool UpdateOlodDuplicate(TempLodConfig lodConfig)
{
var olodConfig = _olodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl);
if (olodConfig == null)
return false;
olodConfig.Lod = Convert.ToInt32(lodConfig.Lod);
return true;
}
}
public class TempLodConfig
{
public int? Agl { get; set; }
public int? Lod { get; set; }
}
public class AglValidationRule : ValidationRule
{
private const int MIN = -2000;
private const int MAX = 99999;
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var agl = 0;
try
{
var valueString = value as string;
if (valueString?.Length > 0)
agl = int.Parse(valueString);
}
catch
{
return new ValidationResult(false, "AGL must be a number.");
}
if (agl is < MIN or > MAX)
{
return new ValidationResult(false,
$"AGL must be in the range: {MIN} - {MAX}.");
}
return ValidationResult.ValidResult;
}
}
public class LodValidationRule : ValidationRule
{
private const int MIN = 0;
private const int MAX = 400;
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var lod = 0;
try
{
var valueString = value as string;
if (valueString?.Length > 0)
lod = int.Parse(valueString);
}
catch
{
return new ValidationResult(false, "LOD must be a number.");
}
if (lod is < MIN or > MAX)
{
return new ValidationResult(false,
$"LOD must be in the range: {MIN} - {MAX}.");
}
return ValidationResult.ValidResult;
}
}
}

View file

@ -0,0 +1,334 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.HelpDrawer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="1024"
mc:Ignorable="d">
<UserControl.Resources>
<Style
x:Key="TextBlockHeading"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Margin" Value="0,5,0,0" />
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style TargetType="AccessText">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Margin" Value="5,-2,0,0" />
</Style>
<Style TargetType="Line">
<Setter Property="Margin" Value="0,5,0,5" />
</Style>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="0" />
<ScrollBar
Name="PART_VerticalScrollBar"
Grid.Column="1"
Width="10"
MinWidth="10"
Maximum="{TemplateBinding ScrollableHeight}"
Opacity="0.5"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}" />
<ScrollBar
Name="PART_HorizontalScrollBar"
Grid.Row="1"
Grid.Column="0"
Height="10"
MinHeight="10"
Maximum="{TemplateBinding ScrollableWidth}"
Opacity="0.5"
Orientation="Horizontal"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{TemplateBinding HorizontalOffset}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<DockPanel d:DataContext="{d:DesignInstance viewmodel:HelpViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<TreeView
Width="210"
VerticalAlignment="Stretch"
DockPanel.Dock="Left">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="Foreground" Value="#666666" />
<Setter Property="Background" Value="Transparent" />
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White" />
</Style.Resources>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem
Margin="-5,0,0,10"
FontSize="14"
FontWeight="Bold"
Foreground="Gray"
Header="Help"
IsHitTestVisible="False" />
<TreeViewItem
x:Name="CategoryKeyboardCommands"
Margin="0,0,0,10"
Header="Keyboard Commands"
IsSelected="True" />
<TreeViewItem
x:Name="CategoryUserGuide"
Margin="0,0,0,10"
Header="User Guide" />
<TreeViewItem
x:Name="CategoryDownloadLatestRelease"
Margin="0,0,0,10"
Header="Download Latest Release" />
<TreeViewItem
x:Name="CategorySupport"
Margin="0,0,0,10"
Header="Support" />
<TreeViewItem
x:Name="CategoryAbout"
Margin="0,0,0,10"
Header="About" />
</TreeView>
</ScrollViewer>
<ScrollViewer
Width="780"
Height="565"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="0,-5,0,0">
<!-- Keyboard Commands -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryKeyboardCommands, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Keyboard Commands</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<TextBlock Margin="0,0,0,20" TextWrapping="Wrap">
To configure a pop out panel, first click on the move and resize icon<InlineUIContainer>
<materialDesign:PackIcon Kind="MoveResize" />
</InlineUIContainer>
for the panel. You can then use keyboard commands below to adjust pop out panel when the icon turns green. To end panel configuration using keyboard commands, just click the icon<InlineUIContainer>
<materialDesign:PackIcon Kind="MoveResize" />
</InlineUIContainer>
again to end panel adjustment.</TextBlock>
<TextBlock>
<Run Foreground="LightSkyBlue">Up Arrow</Run>
- Move panel up by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Down Arrow</Run>
- Move panel down by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Left Arrow</Run>
- Move panel left by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Right Arrow</Run>
- Move panel right by 10 pixels<LineBreak /><LineBreak />
<Run Foreground="LightSkyBlue">Shift + Up Arrow</Run>
- Move panel up by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Down Arrow</Run>
- Move panel down by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Left Arrow</Run>
- Move panel left by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Right Arrow</Run>
- Move panel right by 1 pixel<LineBreak /><LineBreak />
<Run Foreground="LightSkyBlue">Ctrl + Up Arrow</Run>
- Decrease height by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Ctrl + Down Arrow</Run>
- Increase height by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Ctrl + Left Arrow</Run>
- Decrease width by 10 pixels<LineBreak />
<Run Foreground="LightSkyBlue">Ctrl + Right Arrow</Run>
- Increase width by 10 pixels<LineBreak /><LineBreak />
<Run Foreground="LightSkyBlue">Shift + Ctrl + Up Arrow</Run>
- Decrease height by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Ctrl + Down Arrow</Run>
- Increase height by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Ctrl + Left Arrow</Run>
- Decrease width by 1 pixel<LineBreak />
<Run Foreground="LightSkyBlue">Shift + Ctrl + Right Arrow</Run>
- Increase width by 1 pixel<LineBreak />
</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- User Guide -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryUserGuide, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">User Guide</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
</WrapPanel>
<TextBlock Width="Auto">
<Hyperlink
NavigateUri="Getting Started"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Getting Started" />
</Hyperlink>
</TextBlock>
<TextBlock Width="Auto" Margin="0,10,0,0">
<Hyperlink
NavigateUri="User Guide"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="User Guide" />
</Hyperlink>
</TextBlock>
</WrapPanel>
<!-- Download Latest -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryDownloadLatestRelease, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Download Latest Release</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
</WrapPanel>
<TextBlock Width="Auto">
<Hyperlink
NavigateUri="Download Latest GitHub"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Download latest release (GitHub)" />
</Hyperlink>
</TextBlock>
<TextBlock Width="Auto" Margin="0,10,0,0">
<Hyperlink
NavigateUri="Download Latest FlightsimTo"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Download latest release (Flightsim.to)" />
</Hyperlink>
</TextBlock>
</WrapPanel>
<!-- Support -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategorySupport, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Support</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
</WrapPanel>
<TextBlock Width="Auto" Margin="0,0,0,0">
<Hyperlink
NavigateUri="Open Data Folder"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Open application data file folder" />
</Hyperlink>
</TextBlock>
<TextBlock Width="Auto" Margin="0,10,0,0">
<Hyperlink
NavigateUri="Download VCC Library"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Download VC++ Library that is required by SimConnect to establish connection to MSFS" />
</Hyperlink>
</TextBlock>
<Button
Width="190"
Margin="0,10,0,0"
HorizontalAlignment="Left"
Command="{Binding DeleteAppCacheCommand}"
Content="Delete Application Cache"
IsEnabled="{Binding HasOrphanAppCache}"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="This will delete all orphan application cache files in your Windows user account 'AppData/Local/Temp/.net/MSFSPopoutPanelManager' folder" />
<!--<Button
Width="280"
Margin="0,10,0,0"
HorizontalAlignment="Left"
Command="{Binding RollBackCommand}"
Content="Rollback to previous version 3.4.6.0321"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="This will rollback the application to previous version 3.4.6.0321. All changes since installing the latest v4.0.0 update will be lost."
Visibility="{Binding IsRollBackCommandVisible, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" />-->
</WrapPanel>
<!-- About -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryAbout, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">About</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
</WrapPanel>
<TextBlock Width="Auto" Margin="0,0,0,0">
<Hyperlink
NavigateUri="Version Info"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<WrapPanel>
<WrapPanel Margin="0">
<Label
Margin="0"
Padding="0"
Content="Version" />
<Label
Margin="5,0,0,0"
Padding="0"
Content="{Binding ApplicationVersion}" />
</WrapPanel>
</WrapPanel>
</Hyperlink>
</TextBlock>
<Label
Margin="0,5,0,0"
Padding="0"
Content="© 2022 Stanley Kwok. All rights reserved." />
<TextBlock Width="Auto" Margin="0,10,0,0">
<Hyperlink
NavigateUri="License"
RequestNavigate="Hyperlink_RequestNavigate"
Style="{StaticResource MaterialDesignBody2Hyperlink}">
<TextBlock Text="Public Release License" />
</Hyperlink>
</TextBlock>
</WrapPanel>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,28 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class HelpDrawer
{
private readonly HelpViewModel _viewModel;
public HelpDrawer()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<HelpViewModel>();
Loaded += (_, _) => { DataContext = _viewModel; };
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
_viewModel.HyperLinkCommand.Execute(e.Uri.ToString());
}
}
}

View file

@ -0,0 +1,25 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.EditPanelSourceButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
</UserControl.Resources>
<DockPanel
Width="35"
Margin="0"
VerticalAlignment="Center"
d:DataContext="{d:DesignInstance viewModel:PopOutPanelSourceCardViewModel}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Foreground="{Binding DataItem.PanelSource.Color}"
Kind="CrosshairsGps" />
</DockPanel>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
public partial class EditPanelSourceButton
{
public EditPanelSourceButton()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,43 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.MoveAndResizePanelButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
</UserControl.Resources>
<ToggleButton
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
d:DataContext="{d:DesignInstance viewModel:PopOutPanelConfigCardViewModel}"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=MoveResize,
Size={StaticResource IconSize}}"
Background="Transparent"
Command="{Binding MoveResizePanelCommand}"
IsChecked="{Binding DataItem.IsEditingPanel, Mode=TwoWay}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Use keyboard commands to edit panel size &#x0a;and location. Please see help for more info.">
<ToggleButton.Style>
<Style BasedOn="{StaticResource MaterialDesignActionSecondaryToggleButton}" TargetType="ToggleButton">
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsEditingPanel}" Value="True">
<Setter Property="Foreground" Value="LimeGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsEditingPanel}" Value="False">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="MoveResize" />
</ToggleButton>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
public partial class MoveAndResizePanelButton
{
public MoveAndResizePanelButton()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,52 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.MoveUpDownButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="UpDownButtonSize">22</system:Double>
</UserControl.Resources>
<StackPanel
Width="25"
Height="52"
Margin="0"
VerticalAlignment="Center"
d:DataContext="{d:DesignInstance viewModel:PopOutPanelConfigCardViewModel}">
<WrapPanel
Height="26"
Margin="0,4,0,-4"
VerticalAlignment="Center">
<Button
x:Name="BtnMovePanelUp"
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Command="{Binding MovePanelUpCommand}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Move panel Up">
<materialDesign:PackIcon
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Kind="CaretUp" />
</Button>
</WrapPanel>
<WrapPanel Height="26" VerticalAlignment="Center">
<Button
x:Name="BtnMovePanelDown"
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Command="{Binding MovePanelDownCommand}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Move panel down">
<materialDesign:PackIcon
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Kind="CaretDown" />
</Button>
</WrapPanel>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
public partial class MoveUpDownButton
{
public MoveUpDownButton()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,145 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.PanelConfigField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="UpDownIconSize">14</system:Double>
<system:Double x:Key="UpDownButtonSize">22</system:Double>
<Style BasedOn="{StaticResource MaterialDesignFloatingHintTextBox}" TargetType="TextBox">
<Setter Property="materialDesign:HintAssist.FloatingScale" Value="1" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Padding" Value="0" />
<EventSetter Event="KeyDown" Handler="TextBox_KeyDown" />
<EventSetter Event="GotFocus" Handler="TextBox_GotFocus" />
</Style>
<Style
x:Key="BtnPlusMinus"
BasedOn="{StaticResource MaterialDesignIconForegroundButton}"
TargetType="Button">
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="30" />
<Setter Property="Margin" Value="0,0,5,0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Command" Value="{Binding PlusMinusCommand}" />
</Style>
</UserControl.Resources>
<StackPanel d:DataContext="{d:DesignInstance viewModel:PanelConfigFieldViewModel}" Orientation="Horizontal">
<TextBox
x:Name="TxtBoxData"
Width="50"
Margin="0,0,0,0"
materialDesign:HintAssist.Hint="{Binding BindingPath}"
IsEnabled="{c:Binding '!DataItem.FullScreen'}"
PreviewTextInput="TxtBox_NumbersOnly"
SourceUpdated="Data_SourceUpdated"
TextChanged="TxtBox_NumbersOnlyTextChanged" />
<materialDesign:PopupBox
x:Name="PopupBoxAdjustment"
Padding="5"
IsEnabled="{c:Binding '!DataItem.FullScreen'}"
PlacementMode="RightAndAlignMiddles"
PopupHorizontalOffset="-20"
PopupUniformCornerRadius="10"
StaysOpen="True">
<materialDesign:PopupBox.ToggleContent>
<Button
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Margin="-4,12,0,0"
Click="BtnPopupBoxOpen_Click">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Foreground="White"
Kind="ArrowUpDown" />
</Button>
</materialDesign:PopupBox.ToggleContent>
<StackPanel
Width="155"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label
Width="60"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="1 pixel" />
<Button
CommandParameter="1"
Style="{StaticResource BtnPlusMinus}"
ToolTip="Add 1 pixel">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Kind="Plus" />
</Button>
<Button
CommandParameter="-1"
Style="{StaticResource BtnPlusMinus}"
ToolTip="Subtract 1 pixel">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Kind="Minus" />
</Button>
</StackPanel>
<Separator
Height="2"
Margin="0"
Padding="0" />
<StackPanel Orientation="Horizontal">
<Label
Width="60"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="10 pixels" />
<Button
CommandParameter="10"
Style="{StaticResource BtnPlusMinus}"
ToolTip="Add 10 pixels">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Kind="Plus" />
</Button>
<Button
CommandParameter="-10"
Style="{StaticResource BtnPlusMinus}"
ToolTip="Subtract 10 pixels">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Kind="Minus" />
</Button>
</StackPanel>
</StackPanel>
<Button
Width="{StaticResource UpDownButtonSize}"
Height="{StaticResource UpDownButtonSize}"
Margin="5,0,0,0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Click="BtnPopupBoxClose_Click"
Style="{StaticResource MaterialDesignIconForegroundButton}">
<materialDesign:PackIcon
Width="{StaticResource UpDownIconSize}"
Height="{StaticResource UpDownIconSize}"
Kind="CloseBoxOutline" />
</Button>
</StackPanel>
</materialDesign:PopupBox>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,112 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
public partial class PanelConfigField
{
private readonly PanelConfigFieldViewModel _viewModel;
public static readonly DependencyProperty DataItemProperty = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PanelConfigField));
public static readonly DependencyProperty BindingPathProperty = DependencyProperty.Register(nameof(BindingPath), typeof(string), typeof(PanelConfigField));
public static readonly RoutedEvent SourceUpdatedEvent = EventManager.RegisterRoutedEvent("SourceUpdatedEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PanelConfigField));
public PanelConfigField()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
InitializeComponent();
return;
}
_viewModel = App.AppHost.Services.GetRequiredService<PanelConfigFieldViewModel>();
Loaded += (_, _) =>
{
_viewModel.DataItem = DataItem;
_viewModel.BindingPath = BindingPath;
_viewModel.SourceUpdatedEvent = SourceUpdatedEvent;
this.DataContext = _viewModel;
InitializeComponent();
var binding = new Binding($"DataItem.{BindingPath}")
{
Mode = BindingMode.TwoWay,
NotifyOnSourceUpdated = true
};
TxtBoxData?.SetBinding(TextBox.TextProperty, binding);
};
}
public PanelConfig DataItem
{
get => (PanelConfig)GetValue(DataItemProperty);
set => SetValue(DataItemProperty, value);
}
public string BindingPath
{
get => (string)GetValue(BindingPathProperty);
set => SetValue(BindingPathProperty, value);
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter)
return;
Keyboard.ClearFocus();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this.Parent), this.Parent as IInputElement);
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
TxtBoxData.Dispatcher.BeginInvoke(new Action(() => TxtBoxData.SelectAll()));
}
private string _oldText;
private void TxtBox_NumbersOnly(object sender, TextCompositionEventArgs e)
{
e.Handled = !(int.TryParse(e.Text, out _) || (e.Text.Trim() == "-"));
if (!e.Handled)
_oldText = ((TextBox)sender).Text;
}
private void TxtBox_NumbersOnlyTextChanged(object sender, TextChangedEventArgs e)
{
var txtBox = (TextBox)sender;
if (String.IsNullOrEmpty(txtBox.Text))
txtBox.Text = "0";
else if (!(int.TryParse(txtBox.Text, out _) || (txtBox.Text.Trim() == "-")))
{
txtBox.Text = _oldText;
}
}
private void Data_SourceUpdated(object sender, DataTransferEventArgs e)
{
_viewModel.DataUpdatedCommand.Execute(null);
}
private void BtnPopupBoxOpen_Click(object sender, RoutedEventArgs e)
{
PopupBoxAdjustment.IsPopupOpen = true;
}
private void BtnPopupBoxClose_Click(object sender, RoutedEventArgs e)
{
PopupBoxAdjustment.IsPopupOpen = false;
}
}
}

View file

@ -0,0 +1,84 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.PanelTargetField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="LeftRightIconSize">18</system:Double>
<converter:StringEmptyConverter x:Key="StringEmptyConverter" />
<Style
x:Key="BtnLeftRight"
BasedOn="{StaticResource MaterialDesignIconForegroundButton}"
TargetType="Button">
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="30" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</UserControl.Resources>
<StackPanel
Width="220"
HorizontalAlignment="Center"
d:DataContext="{d:DesignInstance viewModel:PopOutPanelSourceCardViewModel}"
Orientation="Horizontal">
<TextBox
Width="140"
Margin="65,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Fixed Camera"
BorderBrush="Transparent"
IsHitTestVisible="False"
IsReadOnly="True"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.FixedCameraConfig.Name, Converter={StaticResource StringEmptyConverter}, ConverterParameter='N/A'}"
Visibility="{c:Binding '!DataItem.IsEditingPanel'}" />
<StackPanel
Margin="25,0,0,0"
HorizontalAlignment="Center"
Orientation="Horizontal"
Visibility="{c:Binding 'DataItem.IsEditingPanel'}">
<Button
x:Name="BtnPrevCameraSelection"
Margin="10,12,0,0"
Click="PopupBoxCameraSelectionPrev_Clicked"
Style="{StaticResource BtnLeftRight}">
<materialDesign:PackIcon
Width="{StaticResource LeftRightIconSize}"
Height="{StaticResource LeftRightIconSize}"
Kind="ArrowLeft" />
</Button>
<ComboBox
x:Name="ComboBoxCameraSelection"
Width="120"
Padding="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
materialDesign:HintAssist.FloatingHintHorizontalAlignment="Left"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Fixed Camera"
SelectionChanged="ComboBoxCameraSelection_OnSelectionChanged"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
<Button
x:Name="BtnNextCameraSelection"
Margin="0,12,0,0"
Click="PopupBoxCameraSelectionNext_Clicked"
Style="{StaticResource BtnLeftRight}">
<materialDesign:PackIcon
Width="{StaticResource LeftRightIconSize}"
Height="{StaticResource LeftRightIconSize}"
Kind="ArrowRight" />
</Button>
</StackPanel>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,108 @@
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
/// <summary>
/// Interaction logic for PanelTargetField.xaml
/// </summary>
public partial class PanelTargetField
{
public PanelTargetField()
{
InitializeComponent();
this.Loaded += PanelTargetField_Loaded;
}
private void PanelTargetField_Loaded(object sender, RoutedEventArgs e)
{
var dataContext = ((PopOutPanelSourceCardViewModel) this.DataContext);
if (dataContext == null)
return;
dataContext.FixedCameraConfigs.CollectionChanged += (_, _) =>
{
var items = dataContext.FixedCameraConfigs;
this.ComboBoxCameraSelection.ItemsSource = items;
this.ComboBoxCameraSelection.DisplayMemberPath = "Name";
var index = items.ToList().FindIndex(x => x.Id == dataContext.DataItem.FixedCameraConfig.Id);
if (index == -1)
return;
this.ComboBoxCameraSelection.SelectedIndex = index;
};
}
private void PopupBoxCameraSelectionPrev_Clicked(object sender, RoutedEventArgs e)
{
var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext);
var selectedItem = dataContext.DataItem.FixedCameraConfig;
// rebinding the camera list with erase the selected item, need the next line to set the selectedItem
var items = dataContext.FixedCameraConfigs;
if (selectedItem == null)
return;
var index = items.ToList().FindIndex(x => x.Name == selectedItem.Name);
if (index == -1)
return;
if (index == 0)
index = items.Count - 1;
else
index -= 1;
this.ComboBoxCameraSelection.SelectedIndex = index;
dataContext.DataItem.FixedCameraConfig = items[index]; // assign and save item
//dataContext.SetCamera();
}
private void PopupBoxCameraSelectionNext_Clicked(object sender, RoutedEventArgs e)
{
var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext);
var selectedItem = dataContext.DataItem.FixedCameraConfig;
// rebinding the camera list with erase the selected item, need the next line to set the selectedItem
var items = dataContext.FixedCameraConfigs;
if (selectedItem == null)
return;
var index = items.ToList().FindIndex(x => x.Name == selectedItem.Name);
if (index == -1)
index = 0;
if (index == items.Count - 1)
index = 0;
else
index += 1;
this.ComboBoxCameraSelection.SelectedIndex = index;
dataContext.DataItem.FixedCameraConfig = items[index]; // assign and save item
//dataContext.SetCamera();
}
private void ComboBoxCameraSelection_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext);
if (e.AddedItems.Count <= 0)
return;
dataContext.DataItem.FixedCameraConfig = (FixedCameraConfig)e.AddedItems[0];
dataContext.SetCamera();
}
}
}

View file

@ -0,0 +1,40 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard.TouchEnabledButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
</UserControl.Resources>
<ToggleButton
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
d:DataContext="{d:DesignInstance viewModel:PopOutPanelConfigCardViewModel}"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=HandBackRightOutline,
Size={StaticResource IconSize}}"
Background="Transparent"
Command="{Binding TouchEnabledCommand}"
IsChecked="{Binding DataItem.TouchEnabled, Mode=TwoWay}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Toggle panel touch capability">
<ToggleButton.Style>
<Style BasedOn="{StaticResource MaterialDesignActionSecondaryToggleButton}" TargetType="ToggleButton">
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.TouchEnabled}" Value="True">
<Setter Property="Foreground" Value="LimeGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="HandBackRightOffOutline" />
</ToggleButton>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard
{
public partial class TouchEnabledButton
{
public TouchEnabledButton()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,337 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelConfigCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:popOutPanelCard="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard"
xmlns:profileDomain="clr-namespace:MSFSPopoutPanelManager.DomainModel.Profile;assembly=DomainModel"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="860"
MinHeight="40"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
<DataTrigger
x:Key="TriggerIsProfileLocked"
Binding="{Binding ActiveProfile.IsLocked}"
Value="True">
<d:DataTrigger.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="FrameworkElement.IsHitTestVisible" Value="False" />
<Setter Property="FrameworkElement.Opacity" Value="0.8" />
</DataTrigger>
<DataTrigger
x:Key="TriggerIsProfileUnlocked"
Binding="{Binding ActiveProfile.IsLocked}"
Value="False">
<d:DataTrigger.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="FrameworkElement.IsHitTestVisible" Value="True" />
<Setter Property="Button.Foreground" Value="White" />
</DataTrigger>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<StaticResource ResourceKey="TriggerIsProfileLocked" />
<StaticResource ResourceKey="TriggerIsProfileUnlocked" />
</Style.Triggers>
</Style>
<Style
x:Key="ToggleButton"
BasedOn="{StaticResource MaterialDesignSwitchToggleButton}"
TargetType="ToggleButton">
<Setter Property="Margin" Value="4,0,4,0" />
</Style>
<Style
x:Key="TextBlockLabel"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Margin" Value="5,0,40,0" />
<Setter Property="LineHeight" Value="18" />
</Style>
<Style BasedOn="{StaticResource MaterialDesignIconForegroundButton}" TargetType="Button" />
<Style
x:Key="PopOutPanelExpander"
BasedOn="{StaticResource CustomMaterialDesignExpander}"
TargetType="Expander">
<d:Style.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="False">
<Setter Property="BorderBrush" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="True">
<Setter Property="BorderBrush" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="PopOutPanelExpanderVisibility" TargetType="StackPanel">
<d:Style.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="True">
<Setter Property="FrameworkElement.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="{x:Null}">
<Setter Property="FrameworkElement.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="False">
<Setter Property="FrameworkElement.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ErrorPanelExpanderVisibility" TargetType="StackPanel">
<d:Style.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="True">
<Setter Property="FrameworkElement.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="{x:Null}">
<Setter Property="FrameworkElement.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsPopOutSuccess}" Value="False">
<Setter Property="FrameworkElement.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="TxtBlockErrorMessage" TargetType="TextBlock">
<d:Style.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.PanelType}" Value="{x:Static profileDomain:PanelType.CustomPopout}">
<Setter Property="Text" Value="Unable to pop out this panel. Please check the source panel circle defined for this panel is at the correct location and not blocked by other window. Also please check if this panel is a duplicate with another panel. Lastly, please close all instrumentation pop outs that were opened manually." />
<Setter Property="LineHeight" Value="18" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.PanelType}" Value="{x:Static profileDomain:PanelType.BuiltInPopout}">
<Setter Property="Text" Value="Unable to configure this built-in panel. Please make sure this panel has been opened and popped out by the game." />
<Setter Property="LineHeight" Value="18" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="TxtBlockDisableWhenFullScreen"
BasedOn="{StaticResource TextBlockLabel}"
TargetType="TextBlock">
<d:Style.DataContext>
<x:Type Type="viewModel:PopOutPanelConfigCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.FullScreen}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<DockPanel d:DataContext="{d:DesignInstance viewModel:PopOutPanelConfigCardViewModel}">
<Expander
x:Name="RootExpander"
Width="860"
materialDesign:ExpanderAssist.HorizontalHeaderPadding="10,0,10,0"
BorderThickness="1">
<Expander.Style>
<Style BasedOn="{StaticResource PopOutPanelExpander}" TargetType="Expander">
<Style.Triggers>
<StaticResource ResourceKey="TriggerIsProfileLocked" />
<StaticResource ResourceKey="TriggerIsProfileUnlocked" />
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Width="805" Orientation="Horizontal">
<StackPanel
Width="24"
Height="52"
Margin="0"
Style="{StaticResource ErrorPanelExpanderVisibility}">
<materialDesign:PopupBox
x:Name="PopupErrorMessage"
Margin="0,15,0,0"
Padding="5"
PlacementMode="RightAndAlignMiddles"
PopupHorizontalOffset="-10"
PopupUniformCornerRadius="10"
PopupVerticalOffset="15"
StaysOpen="True">
<materialDesign:PopupBox.ToggleContent>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Foreground="Red"
Kind="AlertCircleOutline" />
</materialDesign:PopupBox.ToggleContent>
<TextBlock
Width="450"
Style="{StaticResource TxtBlockErrorMessage}"
TextWrapping="Wrap" />
</materialDesign:PopupBox>
</StackPanel>
<!-- Drag panel handle -->
<StackPanel
Width="24"
Height="52"
Margin="0"
Style="{StaticResource PopOutPanelExpanderVisibility}">
<materialDesign:PackIcon
x:Name="IconDrag"
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="0,15,0,0"
Kind="Menu"
Opacity="0.5" />
</StackPanel>
<!-- Panel name text box -->
<TextBox
x:Name="TxtBoxPanelName"
Width="260"
Margin="8,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Name"
GotFocus="TextBox_GotFocus"
IsEnabled="{Binding DataItem.IsCustomPopOut}"
KeyDown="TextBox_KeyDown"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelName, Mode=TwoWay, NotifyOnSourceUpdated=True}" />
<!-- Panel configurations -->
<StackPanel Width="440" VerticalAlignment="Center">
<StackPanel
x:Name="StackPanelAdjustment"
Width="440"
Orientation="Horizontal"
Visibility="{c:Binding 'DataItem.IsBuiltInPopOut or DataItem.IsHudBarWindow or DataItem.IsNumPadWindow or (DataItem.IsCustomPopOut and DataItem.PanelSource.X != null)'}">
<popOutPanelCard:PanelConfigField
Margin="20,0,0,0"
BindingPath="Top"
DataItem="{Binding DataItem}"
IsEnabled="{c:Binding '!DataItem.IsRefocusDisplay'}"
SourceUpdated="Data_SourceUpdated" />
<popOutPanelCard:PanelConfigField
Margin="20,0,0,0"
BindingPath="Left"
DataItem="{Binding DataItem}"
IsEnabled="{c:Binding '!DataItem.IsRefocusDisplay'}"
SourceUpdated="Data_SourceUpdated" />
<popOutPanelCard:PanelConfigField
Margin="20,0,0,0"
BindingPath="Width"
DataItem="{Binding DataItem}"
IsEnabled="{c:Binding '!DataItem.IsHudBarWindow and !DataItem.IsRefocusDisplay'}"
SourceUpdated="Data_SourceUpdated" />
<popOutPanelCard:PanelConfigField
Margin="20,0,0,0"
BindingPath="Height"
DataItem="{Binding DataItem}"
IsEnabled="{c:Binding '!DataItem.IsHudBarWindow and !DataItem.IsRefocusDisplay'}"
SourceUpdated="Data_SourceUpdated" />
<popOutPanelCard:MoveAndResizePanelButton Margin="12,0,0,0" Visibility="{c:Binding '!DataItem.IsRefocusDisplay'}" />
</StackPanel>
<StackPanel
Width="440"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{c:Binding 'DataItem.IsCustomPopOut and DataItem.PanelSource.X == null'}">
<Label HorizontalAlignment="Center">Please identify panel source</Label>
</StackPanel>
</StackPanel>
<!-- Touch enable panel button -->
<StackPanel Margin="12,0,0,0" VerticalAlignment="Center">
<popOutPanelCard:TouchEnabledButton
Width="{StaticResource IconSize}"
Margin="0"
Visibility="{c:Binding 'DataItem.IsTouchEnablePanel'}" />
</StackPanel>
<!-- Delete panel button -->
<StackPanel Margin="15,0,0,0" VerticalAlignment="Center">
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
Command="{Binding DeletePanelCommand}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Delete panel"
Visibility="{c:Binding 'DataItem.IsDeletablePanel'}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="DeleteOutline" />
</Button>
</StackPanel>
</StackPanel>
</Expander.Header>
<StackPanel
Margin="42,8,24,16"
Orientation="Horizontal"
TextBlock.Foreground="{DynamicResource MaterialDesignBody}">
<WrapPanel>
<ToggleButton
x:Name="TglBtnAlwaysOnTop"
Margin="0,0,0,0"
IsChecked="{Binding DataItem.AlwaysOnTop, Mode=TwoWay, NotifyOnSourceUpdated=True}"
IsEnabled="{c:Binding !DataItem.FullScreen}"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenFullScreen}" ToolTip="Set this panel to be always on top">
Always on Top
</TextBlock>
</WrapPanel>
<WrapPanel>
<ToggleButton
x:Name="TglBtnFullScreen"
Margin="0,0,0,0"
IsChecked="{Binding DataItem.FullScreen, Mode=TwoWay, NotifyOnSourceUpdated=True}"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}" ToolTip="Expand this panel into full screen (emulate keystroke Alt-Enter)">Full Screen Mode</TextBlock>
</WrapPanel>
<WrapPanel>
<ToggleButton
x:Name="TglBtnHideTitlebar"
Margin="0,0,0,0"
IsChecked="{Binding DataItem.HideTitlebar, Mode=TwoWay, NotifyOnSourceUpdated=True}"
IsEnabled="{c:Binding !DataItem.FullScreen}"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenFullScreen}" ToolTip="Hide the title bar for this panel">
Hide Title Bar
</TextBlock>
</WrapPanel>
<WrapPanel>
<ToggleButton
x:Name="TglBtnAutoGameRefocus"
Margin="0,0,0,0"
IsChecked="{Binding DataItem.AutoGameRefocus, Mode=TwoWay, NotifyOnSourceUpdated=True}"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}" ToolTip="Automatic game refocus when clicking this panel or when using touch on this panel">Automatic Game Refocus</TextBlock>
</WrapPanel>
</StackPanel>
</Expander>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,71 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PopOutPanelConfigCard
{
private readonly PopOutPanelConfigCardViewModel _viewModel;
public static readonly DependencyProperty DataItemProperty2 = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PopOutPanelConfigCard));
public PopOutPanelConfigCard()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
InitializeComponent();
return;
}
_viewModel = App.AppHost.Services.GetRequiredService<PopOutPanelConfigCardViewModel>();
Loaded += (_, _) =>
{
_viewModel.DataItem = DataItem;
DataContext = _viewModel;
InitializeComponent();
};
}
public PanelConfig DataItem
{
get => (PanelConfig)GetValue(DataItemProperty2);
set => SetValue(DataItemProperty2, value);
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e is not { Key: Key.Enter })
return;
Keyboard.ClearFocus();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(RootExpander), RootExpander);
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var txtBox = (TextBox)sender;
txtBox.Dispatcher.BeginInvoke(new Action(() => txtBox.SelectAll()));
}
private void Data_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
var param = sender switch
{
PanelConfigField field => field.BindingPath,
ToggleButton button => button.Name.Substring(6),
TextBox box => box.Name.Substring(6),
_ => null
};
if (!string.IsNullOrEmpty(param))
_viewModel.PanelAttributeUpdatedCommand.Execute(param);
}
}
}

View file

@ -0,0 +1,139 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.WPF.DragDrop"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
mc:Ignorable="d">
<UserControl.Resources>
<appUserControl:DummyConverter x:Key="DummyConverter" />
</UserControl.Resources>
<ScrollViewer
Height="350"
materialDesign:ScrollViewerAssist.IsAutoHideEnabled="True"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto">
<DockPanel d:DataContext="{d:DesignInstance viewModel:PopOutPanelListViewModel}">
<ListView
Height="Auto"
Margin="16,0,0,0"
Padding="0"
HorizontalAlignment="Left"
DockPanel.Dock="Top"
ItemsSource="{Binding ActiveProfile.PanelConfigs, Mode=TwoWay}"
Visibility="{c:Binding 'ActiveProfile.IsEditingPanelSource and !ActiveProfile.IsUsedLegacyCameraSystem'}">
<ListView.ItemTemplate>
<DataTemplate>
<appUserControl:PopOutPanelSourceCard Height="Auto" DataItem="{Binding '', Converter={StaticResource DummyConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border
Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ListView
Height="Auto"
Margin="16,0,0,0"
Padding="0"
HorizontalAlignment="Left"
DockPanel.Dock="Top"
ItemsSource="{Binding ActiveProfile.PanelConfigs, Mode=TwoWay}"
Visibility="{c:Binding 'ActiveProfile.IsEditingPanelSource and ActiveProfile.IsUsedLegacyCameraSystem'}">
<ListView.ItemTemplate>
<DataTemplate>
<appUserControl:PopOutPanelSourceLegacyCard Height="Auto" DataItem="{Binding '', Converter={StaticResource DummyConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border
Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ListView
Height="Auto"
Margin="16,0,0,0"
Padding="0"
HorizontalAlignment="Left"
dd:DragDrop.IsDragSource="{c:Binding '!ActiveProfile.IsLocked'}"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
DockPanel.Dock="Top"
ItemsSource="{Binding ActiveProfile.PanelConfigs, Mode=TwoWay}"
Visibility="{c:Binding !ActiveProfile.IsEditingPanelSource}">
<ListView.ItemTemplate>
<DataTemplate>
<appUserControl:PopOutPanelConfigCard Height="Auto" DataItem="{Binding '', Converter={StaticResource DummyConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border
Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</DockPanel>
</ScrollViewer>
</UserControl>

View file

@ -0,0 +1,27 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PopOutPanelList
{
public PopOutPanelList()
{
InitializeComponent();
}
}
public class DummyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}

View file

@ -0,0 +1,46 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelListEmpty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<StackPanel Height="335" Margin="10,0,10,0">
<StackPanel HorizontalAlignment="Right" Orientation="Vertical">
<materialDesign:PackIcon
Width="40"
Height="40"
Margin="0,0,45,0"
HorizontalAlignment="Right"
Foreground="white"
Kind="ArrowUpBoldOutline" />
<TextBlock
Width="80"
Margin="0,5,24,0"
FontSize="16"
Foreground="gray"
TextAlignment="Center"
TextWrapping="Wrap">
Click here to add
</TextBlock>
</StackPanel>
<TextBlock
Margin="0,35,0,0"
HorizontalAlignment="Center"
FontSize="24"
Foreground="gray">
Add pop out panels to aircraft profile
</TextBlock>
<TextBlock
Margin="0,0,0,0"
HorizontalAlignment="Center"
FontSize="16"
Foreground="gray"
TextWrapping="Wrap">
Identifying instrumentation source panel location in the game.<LineBreak />
You must be in cockpit view to start this process.</TextBlock>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PopOutPanelListEmpty
{
public PopOutPanelListEmpty()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,138 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelSourceCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:popOutPanelCard="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="860"
MinHeight="54"
mc:Ignorable="d">
<UserControl.Resources>
<converter:StringEmptyConverter x:Key="StringEmptyConverter" />
<system:Double x:Key="IconSize">22</system:Double>
<Style BasedOn="{StaticResource MaterialDesignIconForegroundButton}" TargetType="Button" />
<Style
x:Key="PopOutPanelExpander"
BasedOn="{StaticResource CustomMaterialDesignExpander}"
TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsSelectedPanelSource}" Value="True">
<d:DataTrigger.DataContext>
<x:Type Type="viewModel:PopOutPanelSourceCardViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="Background" Value="#525252" />
<Setter Property="Opacity" Value="0.9" />
</DataTrigger>
<DataTrigger Binding="{Binding DataItem.IsEditingPanel}" Value="True">
<d:DataTrigger.DataContext>
<x:Type Type="viewModel:PopOutPanelSourceCardViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="Background" Value="#525252" />
<Setter Property="Opacity" Value="0.9" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<DockPanel d:DataContext="{d:DesignInstance viewModel:PopOutPanelSourceCardViewModel}">
<Expander
x:Name="RootExpander"
Width="860"
materialDesign:ExpanderAssist.HorizontalHeaderPadding="10,0,10,0"
BorderThickness="1"
PreviewMouseLeftButtonUp="RootExpander_OnPreviewMouseLeftButtonUp"
Style="{StaticResource PopOutPanelExpander}">
<Expander.Header>
<StackPanel Width="805" Orientation="Horizontal">
<StackPanel Width="24" Height="52" />
<!-- Panel name text box -->
<TextBox
x:Name="TxtBoxPanelName"
Width="260"
Margin="8,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Name"
GotFocus="TextBox_GotFocus"
IsEnabled="{c:Binding 'DataItem.IsCustomPopOut'}"
KeyDown="TextBox_KeyDown"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelName, Mode=TwoWay, NotifyOnSourceUpdated=True}" />
<TextBox
Width="85"
Margin="20,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Src Left"
BorderBrush="Transparent"
IsHitTestVisible="False"
IsReadOnly="True"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelSource.X, Converter={StaticResource StringEmptyConverter}, ConverterParameter='N/A'}"
Visibility="{c:Binding 'DataItem.IsCustomPopOut'}" />
<TextBox
Width="85"
Margin="10,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Src Top"
BorderBrush="Transparent"
IsHitTestVisible="False"
IsReadOnly="True"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelSource.Y, Converter={StaticResource StringEmptyConverter}, ConverterParameter='N/A'}"
Visibility="{c:Binding 'DataItem.IsCustomPopOut'}" />
<StackPanel
Width="220"
Margin="5,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{c:Binding 'DataItem.IsCustomPopOut'}">
<popOutPanelCard:PanelTargetField />
</StackPanel>
<!-- New panel -->
<Button
x:Name="BtnIdentifySourcePanel"
Width="80"
Margin="2,0,0,0"
VerticalAlignment="Center"
Command="{Binding AddPanelSourceLocationCommand}"
Content="Identify"
Foreground="White"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="Identify source aircraft instrumentation panel location"
Visibility="{c:Binding 'DataItem.IsCustomPopOut and DataItem.PanelSource.X == null'}" />
<!-- Existing Panel -->
<materialDesign:PackIcon
x:Name="BtnShowSourcePanel"
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="31,0,0,0"
VerticalAlignment="Center"
Foreground="{Binding DataItem.PanelSource.Color}"
Kind="Crosshairs"
Visibility="{c:Binding 'DataItem.IsDeletablePanel and DataItem.PanelSource.X != null'}" />
</StackPanel>
</Expander.Header>
</Expander>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,83 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PopOutPanelSourceCard
{
private readonly PopOutPanelSourceCardViewModel _viewModel;
public static readonly DependencyProperty DataItemProperty = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PopOutPanelSourceCard));
public PopOutPanelSourceCard()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
InitializeComponent();
return;
}
_viewModel = App.AppHost.Services.GetRequiredService<PopOutPanelSourceCardViewModel>();
Loaded += (_, _) =>
{
_viewModel.DataItem = DataItem;
DataContext = _viewModel;
InitializeComponent();
};
}
public PanelConfig DataItem
{
get => (PanelConfig)GetValue(DataItemProperty);
set => SetValue(DataItemProperty, value);
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e is not { Key: Key.Enter })
return;
Keyboard.ClearFocus();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(RootExpander), RootExpander);
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var txtBox = (TextBox)sender;
txtBox.Dispatcher.BeginInvoke(new Action(() => txtBox.SelectAll()));
}
private void Data_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
string param = null;
if (sender is TextBox box)
param = box.Name.Substring(6);
if (!string.IsNullOrEmpty(param))
_viewModel.PanelAttributeUpdatedCommand.Execute(param);
}
private void RootExpander_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_viewModel.DataItem.PanelType != PanelType.CustomPopout)
return;
var fe = e.OriginalSource as FrameworkElement;
if (fe?.Name != "HeaderSiteContent" && fe?.Name != "BtnShowSourcePanel")
return;
foreach (var panelConfig in _viewModel.ActiveProfile.PanelConfigs)
panelConfig.IsSelectedPanelSource = false;
_viewModel.DataItem.IsSelectedPanelSource = true;
_viewModel.EditPanelSourceCommand.Execute();
}
}
}

View file

@ -0,0 +1,124 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelSourceLegacyCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="860"
MinHeight="54"
mc:Ignorable="d">
<UserControl.Resources>
<converter:StringEmptyConverter x:Key="StringEmptyConverter" />
<system:Double x:Key="IconSize">22</system:Double>
<Style BasedOn="{StaticResource MaterialDesignIconForegroundButton}" TargetType="Button" />
<Style
x:Key="PopOutPanelExpander"
BasedOn="{StaticResource CustomMaterialDesignExpander}"
TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding DataItem.IsSelectedPanelSource}" Value="True">
<d:DataTrigger.DataContext>
<x:Type Type="viewModel:PopOutPanelSourceLegacyCardViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="Background" Value="#525252" />
<Setter Property="Opacity" Value="0.9" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<DockPanel d:DataContext="{d:DesignInstance viewModel:PopOutPanelSourceLegacyCardViewModel}">
<Expander
x:Name="RootExpander"
Width="860"
materialDesign:ExpanderAssist.HorizontalHeaderPadding="10,0,10,0"
BorderThickness="1"
Style="{StaticResource PopOutPanelExpander}">
<Expander.Header>
<StackPanel Width="805" Orientation="Horizontal">
<StackPanel Width="24" Height="52" />
<!-- Panel name text box -->
<TextBox
x:Name="TxtBoxPanelName"
Width="260"
Margin="8,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Name"
GotFocus="TextBox_GotFocus"
IsEnabled="{c:Binding 'DataItem.IsCustomPopOut'}"
KeyDown="TextBox_KeyDown"
SourceUpdated="Data_SourceUpdated"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelName, Mode=TwoWay, NotifyOnSourceUpdated=True}" />
<TextBox
Width="85"
Margin="20,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Src Left"
BorderBrush="Transparent"
IsHitTestVisible="False"
IsReadOnly="True"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelSource.X, Converter={StaticResource StringEmptyConverter}, ConverterParameter='N/A'}"
Visibility="{c:Binding 'DataItem.IsCustomPopOut'}" />
<TextBox
Width="85"
Margin="10,0,0,0"
Padding="0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
materialDesign:HintAssist.FloatingScale="1"
materialDesign:HintAssist.Hint="Panel Src Top"
BorderBrush="Transparent"
IsHitTestVisible="False"
IsReadOnly="True"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding DataItem.PanelSource.Y, Converter={StaticResource StringEmptyConverter}, ConverterParameter='N/A'}"
Visibility="{c:Binding 'DataItem.IsCustomPopOut'}" />
<StackPanel Width="225" />
<!-- New panel -->
<Button
x:Name="BtnIdentifySourcePanel"
Width="80"
Margin="2,0,0,0"
VerticalAlignment="Center"
Command="{Binding AddPanelSourceLocationCommand}"
Content="Identify"
Foreground="White"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="Identify source aircraft instrumentation panel location"
Visibility="{c:Binding 'DataItem.IsCustomPopOut and DataItem.PanelSource.X == null'}" />
<!-- Existing Panel -->
<materialDesign:PackIcon
x:Name="BtnShowSourcePanel"
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="31,0,0,0"
VerticalAlignment="Center"
Foreground="{Binding DataItem.PanelSource.Color}"
Kind="Crosshairs"
Visibility="{c:Binding 'DataItem.IsDeletablePanel and DataItem.PanelSource.X != null'}" />
</StackPanel>
</Expander.Header>
</Expander>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,66 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PopOutPanelSourceLegacyCard
{
private readonly PopOutPanelSourceLegacyCardViewModel _viewModel;
public static readonly DependencyProperty DataItemProperty = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PopOutPanelSourceLegacyCard));
public PopOutPanelSourceLegacyCard()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
InitializeComponent();
return;
}
_viewModel = App.AppHost.Services.GetRequiredService<PopOutPanelSourceLegacyCardViewModel>();
Loaded += (_, _) =>
{
_viewModel.DataItem = DataItem;
this.DataContext = _viewModel;
InitializeComponent();
};
}
public PanelConfig DataItem
{
get => (PanelConfig)GetValue(DataItemProperty);
set => SetValue(DataItemProperty, value);
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e is not { Key: Key.Enter })
return;
Keyboard.ClearFocus();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(RootExpander), RootExpander);
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var txtBox = (TextBox)sender;
txtBox.Dispatcher.BeginInvoke(new Action(() => txtBox.SelectAll()));
}
private void Data_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
string param = null;
if (sender is TextBox box)
param = box.Name.Substring(6);
if (!string.IsNullOrEmpty(param))
_viewModel.PanelAttributeUpdatedCommand.Execute(param);
}
}
}

View file

@ -0,0 +1,663 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.PreferenceDrawer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:localcontrol="clr-namespace:MSFSPopoutPanelManager.MainApp.CustomControl"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="1024"
mc:Ignorable="d">
<UserControl.Resources>
<Style
x:Key="TextBlockHeading"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Margin" Value="0,5,0,0" />
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style
x:Key="ToggleButton"
BasedOn="{StaticResource MaterialDesignSwitchToggleButton}"
TargetType="ToggleButton">
<Setter Property="Margin" Value="4,0,4,0" />
</Style>
<Style
x:Key="TextBlockLabel"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Width" Value="700" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="LineHeight" Value="20" />
</Style>
<Style TargetType="Line">
<Setter Property="Margin" Value="0,5,0,5" />
</Style>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<DockPanel d:DataContext="{d:DesignInstance viewmodel:ApplicationViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<TreeView
Width="210"
VerticalAlignment="Stretch"
DockPanel.Dock="Left">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="Foreground" Value="#666666" />
<Setter Property="Background" Value="Transparent" />
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="White" />
</Style.Resources>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem
Margin="-5,0,0,10"
Padding="0"
FontSize="14"
FontWeight="Bold"
Foreground="Gray"
Header="Preferences"
IsHitTestVisible="False" />
<TreeViewItem
x:Name="CategoryGeneralSettings"
Margin="0,0,0,10"
Padding="0"
Header="General Settings"
IsSelected="True" />
<TreeViewItem
x:Name="CategoryAutoPopOutPanelSettings"
Margin="0,0,0,10"
Header="Auto Pop Out Panel Settings" />
<TreeViewItem
x:Name="CategoryPopOutSettings"
Margin="0,0,0,10"
Header="Pop Out Settings" />
<TreeViewItem
x:Name="CategoryGameRefocusSettings"
Margin="0,0,0,10"
Header="Game Refocus Settings" />
<TreeViewItem
x:Name="CategoryKeyboardShortcutSettings"
Margin="0,0,0,10"
Header="Keyboard Shortcut Settings" />
<TreeViewItem
x:Name="CategoryTouchSettings"
Margin="0,0,0,10"
Header="Touch Settings" />
<TreeViewItem
x:Name="CategoryTrackIRSettings"
Margin="0,0,0,10"
Header="Track IR Settings" />
<TreeViewItem
x:Name="CategoryWindowedModeSettings"
Margin="0,0,0,10"
Header="Windowed Mode Settings" />
<TreeViewItem
x:Name="CategoryDynamicLodSettings"
Margin="0,0,0,10"
Header="Dynamic LOD Settings"
Visibility="{c:Binding LocalCompileOnly}" />
</TreeView>
</ScrollViewer>
<ScrollViewer
Width="780"
Height="565"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="0,-4,0,0">
<!-- General Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryGeneralSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel
Width="Auto"
Margin="0,0,20,20"
Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Always on Top</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Pin the application on top of all open windows.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Start</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.AutoStart, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable auto start application when MSFS starts. This adds a XML config entry in EXE.xml file.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Minimize to Tray</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.MinimizeToTray, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Minimize the application to system tray.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Start Minimized</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.StartMinimized, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Start the application in minimized mode in system tray.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Close When Exiting MSFS</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.AutoClose, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Automatically close the application when exiting MSFS.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Turbo Mode</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.TurboMode, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
<Bold>WARNING!</Bold>
This may not work for all PC. Enable turbo mode to pop out panels as fast as possible. If you have a fast PC, this will let Pop Out Panel Manager executes pop out much faster.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Check for Update</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.GeneralSetting.CheckForUpdate, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable check for application update through Github.</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- Auto Pop Out Panel Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryAutoPopOutPanelSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Auto Pop Out Panels</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.AutoPopOutSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Automatic pop out panels when an aircraft livery is bound to a profile. The following steps will be performed.</TextBlock>
</WrapPanel>
<StackPanel Margin="34,0,0,0" Orientation="Vertical">
<TextBlock Margin="0,10,0,0" Style="{StaticResource TextBlockLabel}">
1. Detect flight start signal using SimConnect.
</TextBlock>
<TextBlock Margin="0,10,0,0" Style="{StaticResource TextBlockLabel}">
2. Wait for cockpit view to appear before executing pop out panel sequence.
</TextBlock>
<TextBlock Margin="0,10,0,0" Style="{StaticResource TextBlockLabel}">
3. If configured for profile on cold start, execute and detect instrumentation power on before executing pop out panel sequence.
</TextBlock>
</StackPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Pop Out Panel Delay</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<StackPanel Orientation="Horizontal">
<localcontrol:NumericUpDown
Width="90"
Height="24"
FontSize="12"
Increment="1"
MaxValue="10"
MinValue="0"
Value="{Binding AppSettingData.ApplicationSetting.AutoPopOutSetting.ReadyToFlyDelay, Mode=TwoWay}" />
<TextBlock
Width="640"
Margin="10,0,0,0"
Style="{StaticResource TextBlockLabel}">
Amount of time in seconds to delay auto pop out panels from starting after ready to fly button has been pressed automatically. Extending this delay helps resolve auto pop out failure because cockpit has not been loaded completely yet on slower PC.
</TextBlock>
</StackPanel>
</WrapPanel>
</WrapPanel>
<!-- Pop Out Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryPopOutSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<WrapPanel Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Auto Panning (For use with legacy custom camera to select panel source only)</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AutoPanning.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable automatic panning of cockpit view when popping out panels. Auto Panning remembers the custom cockpit camera angle you used when defining the locations of pop out panel.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AutoPanning.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<StackPanel
Width="Auto"
Margin="44,15,0,0"
VerticalAlignment="Top"
Orientation="Horizontal">
<WrapPanel>
<Label Content="Ctrl-Alt-" FontSize="14" />
<ComboBox
Width="35"
VerticalAlignment="top"
SelectedValue="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AutoPanning.KeyBinding, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="0" Tag="0" />
<ComboBoxItem Content="1" Tag="1" />
<ComboBoxItem Content="2" Tag="2" />
<ComboBoxItem Content="3" Tag="3" />
<ComboBoxItem Content="4" Tag="4" />
<ComboBoxItem Content="5" Tag="5" />
<ComboBoxItem Content="6" Tag="6" />
<ComboBoxItem Content="7" Tag="7" />
<ComboBoxItem Content="8" Tag="8" />
<ComboBoxItem Content="9" Tag="9" />
</ComboBox>
</WrapPanel>
<TextBlock
Width="600"
Margin="10,3,0,0"
Style="{StaticResource TextBlockLabel}">
Configure key binding for saving and recalling of custom MSFS cockpit camera view when defining the locations of pop out panel. Requires binding keystroke to custom camera in MSFS control setting. (Default: Ctrl-Alt-0 to save and Alt-0 to load).
</TextBlock>
</StackPanel>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Minimize Pop Out Panel Manager During Pop Out</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.MinimizeDuringPopOut, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Minimize Pop Out Panel Manager during pop out process.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Minimize Pop Out Panel Manager After Pop Out</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.MinimizeAfterPopOut, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Minimize Pop Out Panel Manager after all panels have been popped out.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Active Pause</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AutoActivePause, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable active pause when panels are being popped out.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<WrapPanel Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Return to Predefined Camera View After Pop Out</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AfterPopOutCameraView.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable return to a predefined camera view after pop out.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AfterPopOutCameraView.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="46,10,0,0" Orientation="Horizontal">
<ComboBox
Width="160"
HorizontalAlignment="Left"
VerticalAlignment="Center"
SelectedValue="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AfterPopOutCameraView.CameraView, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="Cockpit Center View" Tag="CockpitCenterView" />
<ComboBoxItem Content="Custom Camera View" Tag="CustomCameraView" />
</ComboBox>
</WrapPanel>
</WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AfterPopOutCameraView.IsEnabledCustomCameraKeyBinding, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<StackPanel Margin="44,10,0,0" Orientation="Horizontal">
<Label Content="Alt-" FontSize="14" />
<ComboBox
Width="35"
HorizontalAlignment="Left"
VerticalAlignment="Top"
SelectedValue="{Binding AppSettingData.ApplicationSetting.PopOutSetting.AfterPopOutCameraView.KeyBinding, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="0" Tag="0" />
<ComboBoxItem Content="1" Tag="1" />
<ComboBoxItem Content="2" Tag="2" />
<ComboBoxItem Content="3" Tag="3" />
<ComboBoxItem Content="4" Tag="4" />
<ComboBoxItem Content="5" Tag="5" />
<ComboBoxItem Content="6" Tag="6" />
<ComboBoxItem Content="7" Tag="7" />
<ComboBoxItem Content="8" Tag="8" />
<ComboBoxItem Content="9" Tag="9" />
</ComboBox>
<TextBlock
Width="620"
Margin="10,3,0,0"
Style="{StaticResource TextBlockLabel}">
Configure key binding for custom camera view to load. Requires binding keystroke to custom camera in MSFS control setting. (Default: Alt-1 to load).
</TextBlock>
</StackPanel>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Tracking of Panels When Profile Is Locked</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.EnablePanelResetWhenLocked, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable tracking of panels to allow panel to go back to its original location when move if the profile is locked. Disable this setting will allow panel to be moved when profile is locked
but the profile setting will be unchanged. With this setting disable, Pop Out Panel Manager will no longer detect pop out panel's movement when profile is locked which may save some CPU cycles.
</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,0" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Pop Out Progress Messages</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.EnablePopOutMessages, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable display of pop out progress messages</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,20,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Pop Out Title Bar Color Customization</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton
x:Name="TglBtnPopOutColorCustomizationEnable"
IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.PopOutTitleBarCustomization.IsEnabled, Mode=TwoWay}"
Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable setting the color of title bar for pop out panel. The color is set in hexadecimal format of RGB color (RRGGBB). For example, black is "#000000" and white is "#FFFFFF".
</TextBlock>
</WrapPanel>
<WrapPanel Visibility="{Binding AppSettingData.ApplicationSetting.PopOutSetting.PopOutTitleBarCustomization.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<Label Margin="42,10,0,0">Color:</Label>
<Label Margin="0,10,0,0">#</Label>
<TextBox
Width="50"
Height="22"
Margin="0,10,0,0"
VerticalAlignment="Top"
materialDesign:TextFieldAssist.CharacterCounterVisibility="Hidden"
AcceptsReturn="False"
IsEnabled="{Binding Path=IsChecked, ElementName=TglBtnPopOutColorCustomizationEnable}"
MaxLength="6"
Style="{StaticResource MaterialDesignTextBox}"
Text="{Binding AppSettingData.ApplicationSetting.PopOutSetting.PopOutTitleBarCustomization.HexColor, Mode=TwoWay}" />
</WrapPanel>
</WrapPanel>
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Use Left Control + Right Control to Pop Out Panel</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.PopOutSetting.UseLeftRightControlToPopOut, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
If your keyboard does not have a Right-Alt key to perform left click to pop out panel, you can map Left Ctrl + Right Ctrl in MSFS control setting to pop out
panel instead. For this feature to work, please map (CTRL + RIGHT CTRL) in Control Options => Miscellaneous => New UI Window Mode in the game
</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- Game Refocus Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryGameRefocusSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Refocus Game Window</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.RefocusSetting.RefocusGameWindow.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Automatically set focus back to game window after a period of inactivity when either clicking on a panel or touching a panel when touch feature is enabled. This will give you flight control back when using pop out panel to overcome the current MSFS limitation. This setting needs to be enabled for each profile and each pop out panel's automatic refocus setting to work.</TextBlock>
</WrapPanel>
<StackPanel
Margin="46,10,0,0"
Orientation="Horizontal"
Visibility="{Binding AppSettingData.ApplicationSetting.RefocusSetting.RefocusGameWindow.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<localcontrol:NumericUpDown
Width="90"
Height="24"
FontSize="12"
Increment="0.1"
MaxValue="10"
MinValue="0.5"
Places="1"
Value="{Binding AppSettingData.ApplicationSetting.RefocusSetting.RefocusGameWindow.Delay, Mode=TwoWay}" />
<TextBlock
Width="620"
Margin="10,0,0,0"
Style="{StaticResource TextBlockLabel}">
Amount of time in seconds to wait for touch inactivity before input focus goes back to game window.
</TextBlock>
</StackPanel>
</WrapPanel>
</WrapPanel>
<!-- Keyboard Shortcut Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryKeyboardShortcutSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,0" Orientation="Vertical">
<WrapPanel Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Keyboard Shortcuts</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.KeyboardShortcutSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Enable using of keyboard shortcuts to control application.</TextBlock>
</WrapPanel>
</WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="{Binding AppSettingData.ApplicationSetting.KeyboardShortcutSetting.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<StackPanel
Width="Auto"
Margin="44,15,0,0"
VerticalAlignment="Top"
Orientation="Horizontal">
<WrapPanel>
<Label Content="Ctrl-Shift-" FontSize="14" />
<ComboBox
Width="40"
VerticalAlignment="top"
SelectedValue="{Binding AppSettingData.ApplicationSetting.KeyboardShortcutSetting.StartPopOutKeyBinding, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="A" Tag="A" />
<ComboBoxItem Content="B" Tag="B" />
<ComboBoxItem Content="C" Tag="C" />
<ComboBoxItem Content="D" Tag="D" />
<ComboBoxItem Content="E" Tag="E" />
<ComboBoxItem Content="F" Tag="F" />
<ComboBoxItem Content="G" Tag="G" />
<ComboBoxItem Content="H" Tag="H" />
<ComboBoxItem Content="I" Tag="I" />
<ComboBoxItem Content="J" Tag="J" />
<ComboBoxItem Content="K" Tag="K" />
<ComboBoxItem Content="L" Tag="L" />
<ComboBoxItem Content="M" Tag="M" />
<ComboBoxItem Content="N" Tag="N" />
<ComboBoxItem Content="O" Tag="O" />
<ComboBoxItem Content="P" Tag="P" />
<ComboBoxItem Content="Q" Tag="Q" />
<ComboBoxItem Content="R" Tag="R" />
<ComboBoxItem Content="S" Tag="S" />
<ComboBoxItem Content="T" Tag="T" />
<ComboBoxItem Content="U" Tag="U" />
<ComboBoxItem Content="V" Tag="V" />
<ComboBoxItem Content="W" Tag="W" />
<ComboBoxItem Content="X" Tag="X" />
<ComboBoxItem Content="Y" Tag="Y" />
<ComboBoxItem Content="Z" Tag="Z" />
</ComboBox>
</WrapPanel>
<TextBlock
Width="600"
Margin="10,3,0,0"
Style="{StaticResource TextBlockLabel}">
Configure key binding to initiate start pop out.
</TextBlock>
</StackPanel>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- Touch Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryTouchSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Touch Down Touch Up Delay</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<localcontrol:NumericUpDown
Width="90"
Height="24"
FontSize="12"
Increment="5"
MaxValue="100"
MinValue="0"
Value="{Binding AppSettingData.ApplicationSetting.TouchSetting.TouchDownUpDelay, Mode=TwoWay}" />
<TextBlock
Width="630"
Margin="10,0,0,0"
Style="{StaticResource TextBlockLabel}">
Amount of time in milliseconds to delay touch down and then touch up event when operating touch enabled panel. If your touch is not registering consistently, increasing this value may help.
</TextBlock>
<TextBlock
Margin="0,10,0,0"
FontSize="14"
TextWrapping="Wrap">
For panel display on direct connected touch monitor, 0 milliseconds work really well.<LineBreak /><LineBreak />
For panel display on a tablet using software such as SpaceDesk, since there is higher latency for touch signal, increasing this value 5ms at a time may compensate for this latency.</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- TrackIR Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryTrackIRSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Disable Track IR</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.TrackIRSetting.AutoDisableTrackIR, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">Automatically disable Track IR during panel selections and pop out process. Track IR will be re-enabled once these processes are completed.</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- Windowed Mode Settings -->
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryWindowedModeSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Resize MSFS Game Window (Used with Windowed Display Mode only)</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.WindowedModeSetting.AutoResizeMsfsGameWindow, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable automatic resize of MSFS game window when using Windowed Display Mode. When playing the game in Windowed Display Mode, this setting is used to resize game window to match original size
and location when panel profile was initially defined. When this setting is first checked, current game window size and location will also be saved automatically.
</TextBlock>
</WrapPanel>
</WrapPanel>
</WrapPanel>
<!-- Dynamic LOD Settings -->
<WrapPanel Visibility="{c:Binding LocalCompileOnly}">
<WrapPanel Orientation="Vertical" Visibility="{Binding ElementName=CategoryDynamicLodSettings, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Margin="0,0,20,20" Orientation="Vertical">
<TextBlock Style="{StaticResource TextBlockHeading}">Dynamically adjust Terrain and Object Level of Details (Not Supported)</TextBlock>
<Line
Stretch="Fill"
Stroke="Gray"
X2="1" />
<TextBlock
Margin="0,10,0,10"
Foreground="Red"
Style="{StaticResource TextBlockLabel}">
*** Experimental and not supported. Use at your own risk since this feature directly modifies MSFS memory. ***
</TextBlock>
<WrapPanel>
<ToggleButton IsChecked="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}">
Enable automatic adjustment of terrain level of detail (TLOD) and object level of detail (OLOD) based on current aircraft's above ground level (in feet).
</TextBlock>
</WrapPanel>
<WrapPanel Visibility="{Binding Path=AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<appUserControl:DynamicLodPreference />
</WrapPanel>
</WrapPanel>
</WrapPanel>
</WrapPanel>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class PreferenceDrawer
{
public PreferenceDrawer()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,540 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.ProfileCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="clr-namespace:MSFSPopoutPanelManager.DomainModel.Profile;assembly=DomainModel"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="900"
Height="535"
mc:Ignorable="d">
<UserControl.Resources>
<appUserControl:StringToHudBarTypeConverter x:Key="StringToHudBarTypeConverter" />
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
<Style
x:Key="ToggleButton"
BasedOn="{StaticResource MaterialDesignSwitchToggleButton}"
TargetType="ToggleButton">
<Setter Property="Margin" Value="4,0,4,0" />
</Style>
<Style
x:Key="TextBlockLabel"
BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="LineHeight" Value="18" />
</Style>
<Style
x:Key="TxtBlockDisableWhenLocked"
BasedOn="{StaticResource TextBlockLabel}"
TargetType="TextBlock">
<d:Style.DataContext>
<x:Type Type="viewmodel:ProfileCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveProfile.IsLocked}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="TxtBlockDisableWhenLockedInner"
BasedOn="{StaticResource TextBlockLabel}"
TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.ProfileData.ActiveProfile.IsLocked, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="TxtBlockDisableWhenLockedOrPanelSourceEdit"
BasedOn="{StaticResource TextBlockLabel}"
TargetType="TextBlock">
<d:Style.DataContext>
<x:Type Type="viewmodel:ProfileCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveProfile.IsLocked}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
<DataTrigger Binding="{Binding ActiveProfile.IsEditingPanelSource}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="CmbBoxDisableWhenLocked"
BasedOn="{StaticResource MaterialDesignComboBox}"
TargetType="ComboBox">
<d:Style.DataContext>
<x:Type Type="viewmodel:ProfileCardViewModel" />
</d:Style.DataContext>
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveProfile.IsLocked}" Value="True">
<Setter Property="Foreground" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid d:DataContext="{d:DesignInstance viewmodel:ProfileCardViewModel}">
<materialDesign:Card
x:Name="RootCard"
Width="900"
Height="535"
UniformCornerRadius="16">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="44" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="55" />
</Grid.RowDefinitions>
<materialDesign:ColorZone
Grid.Row="0"
materialDesign:ElevationAssist.Elevation="Dp2"
DockPanel.Dock="Top"
Mode="PrimaryDark">
<StackPanel Height="44" Margin="16,6,8,0">
<DockPanel>
<StackPanel Width="812" Orientation="Horizontal">
<TextBox
x:Name="TxtBoxProfileTitle"
Margin="8,0,0,0"
FontSize="22"
FontWeight="Medium"
Foreground="White"
KeyDown="TxtBoxProfileTitle_KeyDown"
Text="{Binding ActiveProfile.Name, Mode=TwoWay}">
<TextBox.Style>
<Style BasedOn="{StaticResource MaterialDesignTextBox}" TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ToggleButtonEditProfileTitle, Path=IsChecked}" Value="False">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="IsReadOnly" Value="true" />
<Setter Property="IsHitTestVisible" Value="false" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=ToggleButtonEditProfileTitle, Path=IsChecked}" Value="True">
<Setter Property="IsReadOnly" Value="false" />
<Setter Property="IsHitTestVisible" Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<ToggleButton
x:Name="ToggleButtonEditProfileTitle"
Width="16"
Height="16"
Margin="8,0,0,0"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=PencilOff,
Size=14}"
Click="ToggleButtonEditProfileTitle_Click"
IsChecked="False"
Style="{StaticResource MaterialDesignActionDarkToggleButton}"
ToolTip="Edit aircraft profile name">
<materialDesign:PackIcon
Width="14"
Height="14"
Kind="Pencil" />
</ToggleButton>
</StackPanel>
<Button
x:Name="BtnDeleteProfile"
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Command="{Binding DeleteProfileCommand}"
IsEnabled="True"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Delete profile">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="DeleteOutline" />
</Button>
</DockPanel>
</StackPanel>
</materialDesign:ColorZone>
<DockPanel Grid.Row="1">
<Expander Style="{DynamicResource CustomMaterialDesignExpander}">
<Expander.Header>
<DockPanel>
<StackPanel
Width="600"
DockPanel.Dock="Left"
Orientation="Horizontal">
<TextBlock
Height="20"
Margin="0,0,10,0"
Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Active Aircraft:
</TextBlock>
<TextBlock Height="20" Text="{c:Binding 'FlightSimData.AircraftName == null ? &quot;Aircraft binding information is currently unavailable&quot; : FlightSimData.AircraftName'}">
<TextBlock.Style>
<Style BasedOn="{StaticResource MaterialDesignSubtitle1TextBlock}" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ProfileData.IsAircraftBoundToProfile}" Value="True">
<Setter Property="Foreground" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding ProfileData.IsAircraftBoundToProfile}" Value="False">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ProfileData.IsAllowedAddAircraftBinding}" Value="False">
<Setter Property="Foreground" Value="White" />
<Setter Property="ToolTip" Value="Aircraft is currently bound to another profile" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HasAircraftName}" Value="False">
<Setter Property="Foreground" Value="AntiqueWhite" />
<Setter Property="ToolTip" Value="No aircraft has been loaded by the game yet" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ToggleButton
Width="{StaticResource ButtonSize}"
Height="{StaticResource IconSize}"
Margin="10,0,0,0"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=AirplaneMinus,
Size={StaticResource IconSize}}"
Background="Transparent"
Command="{Binding ToggleAircraftBindingCommand}"
IsChecked="{Binding ProfileData.IsAircraftBoundToProfile, Mode=OneWay}"
Style="{StaticResource MaterialDesignActionSecondaryToggleButton}"
ToolTip="Toggle aircraft binding to profile"
Visibility="{c:Binding 'FlightSimData.HasAircraftName and ProfileData.IsAllowedAddAircraftBinding'}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="AirplanePlus" />
</ToggleButton>
</StackPanel>
<StackPanel Width="200" DockPanel.Dock="Right">
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.Style>
<Style BasedOn="{StaticResource MaterialDesignSubtitle1TextBlock}" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{c:Binding 'FlightSimData.AircraftName != null and AppSettingData.ApplicationSetting.AutoPopOutSetting.IsEnabled and ProfileData.IsAircraftBoundToProfile'}" Value="True">
<Setter Property="Foreground" Value="LightGreen" />
<Setter Property="Text" Value="(Auto Pop Out is Active)" />
</DataTrigger>
<DataTrigger Binding="{c:Binding 'FlightSimData.AircraftName != null and (!AppSettingData.ApplicationSetting.AutoPopOutSetting.IsEnabled or !ProfileData.IsAircraftBoundToProfile)'}" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="Text" Value="(Auto Pop Out is Inactive)" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DockPanel>
</Expander.Header>
<StackPanel Margin="22,0,0,8">
<Separator Margin="0,0,0,10" />
<WrapPanel>
<ToggleButton IsChecked="{Binding ActiveProfile.ProfileSetting.PowerOnRequiredForColdStart, Mode=TwoWay}" Style="{StaticResource ToggleButton}" />
<TextBlock Style="{StaticResource TextBlockLabel}" ToolTip="During cold start, Pop Out Manager will first turn on power and avionics for the aircraft in order &#x0a;to pop out the instrumentation panels. After pop out is completed, Pop Out Manager will then &#x0a;turn off aircraft power and avionics to put the aircraft back to cold and dark state.">Power on is required to pop out panels on cold start (for G1000 based aircraft ONLY if needed)</TextBlock>
</WrapPanel>
<WrapPanel Margin="0,8,0,0">
<ToggleButton
IsChecked="{Binding ActiveProfile.ProfileSetting.IncludeInGamePanels, Mode=TwoWay, NotifyOnTargetUpdated=True}"
IsHitTestVisible="{c:Binding '!ActiveProfile.IsLocked',
Mode=OneWay}"
Style="{StaticResource ToggleButton}"
TargetUpdated="IncludeInGamePanel_TargetUpdated" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLocked}" ToolTip="Add in-game menu bar panels such as VFR Map, Checklist, ATC, etc. to profile to enable panel size and location management and touch support">Include in-game menu bar panels for pop out management and touch screen support</TextBlock>
</WrapPanel>
<WrapPanel Margin="0,8,0,0">
<ToggleButton
IsChecked="{Binding ActiveProfile.ProfileSetting.NumPadConfig.IsEnabled, Mode=TwoWay, NotifyOnTargetUpdated=True}"
IsHitTestVisible="{c:Binding '!ActiveProfile.IsLocked',
Mode=OneWay}"
Style="{StaticResource ToggleButton}"
TargetUpdated="AddNumPad_TargetUpdated" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLocked}" ToolTip="Add a virtual keyboard NumPad to the game that has MSFS focused before sending key command">Add a virtual keyboard NumPad</TextBlock>
</WrapPanel>
<StackPanel Margin="0,8,0,0" Orientation="Horizontal">
<WrapPanel>
<ToggleButton
IsChecked="{Binding ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled, Mode=TwoWay, NotifyOnTargetUpdated=True}"
IsHitTestVisible="{c:Binding '!ActiveProfile.IsLocked',
Mode=OneWay}"
Style="{StaticResource ToggleButton}"
TargetUpdated="AddHudBar_TargetUpdated" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLocked}" ToolTip="Add an HUD bar to the game">Add a HUD Bar</TextBlock>
<ComboBox
x:Name="ComboBoxHudBarType"
Width="130"
Margin="10,-4,0,0"
FontSize="14"
IsEnabled="{c:Binding '!ActiveProfile.IsLocked and ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled',
Mode=OneWay}"
ItemsSource="{Binding HudBarTypes}"
SelectedValue="{Binding ActiveProfile.ProfileSetting.HudBarConfig.HudBarType, Mode=TwoWay, Converter={StaticResource StringToHudBarTypeConverter}}"
Style="{StaticResource CmbBoxDisableWhenLocked}" />
</WrapPanel>
</StackPanel>
<StackPanel Margin="0,8,0,0" Orientation="Vertical">
<WrapPanel>
<ToggleButton
IsChecked="{Binding ActiveProfile.ProfileSetting.RefocusOnDisplay.IsEnabled, Mode=TwoWay, NotifyOnTargetUpdated=True}"
IsHitTestVisible="{c:Binding '!ActiveProfile.IsLocked',
Mode=OneWay}"
Style="{StaticResource ToggleButton}"
TargetUpdated="AddRefocusDisplay_TargetUpdated" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLocked}" ToolTip="Automatically set focus back to game window after a period of inactivity after touching the designated monitor. This is overcome a bug in MSFS where NVIDIA frame generation does not work when focus is not in the game window.">Enable entire monitor display to have game refocus function when touch</TextBlock>
</WrapPanel>
<WrapPanel
Margin="36,0,0,0"
IsHitTestVisible="{c:Binding !ActiveProfile.IsLocked}"
Orientation="Horizontal"
Visibility="{Binding ActiveProfile.ProfileSetting.RefocusOnDisplay.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<WrapPanel Orientation="Vertical" Visibility="{Binding ActiveProfile.ProfileSetting.RefocusOnDisplay.IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<ItemsControl
Margin="0"
Padding="8"
ItemsSource="{Binding ActiveProfile.ProfileSetting.RefocusOnDisplay.Monitors, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type domain:MonitorInfo}">
<WrapPanel Margin="0,0,0,4" Orientation="Horizontal">
<CheckBox
VerticalAlignment="Center"
Command="{Binding DataContext.RefocusDisplaySelectionUpdatedCommand, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}"
CommandParameter="{Binding Name}"
IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<WrapPanel
Margin="8,0,0,0"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Margin="0,0,5,0"
FontWeight="Bold"
Style="{StaticResource TxtBlockDisableWhenLockedInner}"
Text="{Binding Name}" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLockedInner}" Text="(" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLockedInner}" Text="{Binding Width}" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLockedInner}" Text="x" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLockedInner}" Text="{Binding Height}" />
<TextBlock Style="{StaticResource TxtBlockDisableWhenLockedInner}" Text=")" />
</WrapPanel>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</WrapPanel>
<Button
Width="160"
Margin="20,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding RefocusDisplayRefreshedCommand}"
Style="{StaticResource MaterialDesignOutlinedLightButton}">
Refresh Display List
</Button>
</WrapPanel>
</StackPanel>
</StackPanel>
</Expander>
</DockPanel>
<StackPanel Grid.Row="2">
<Separator Margin="0,-1,0,0" />
<WrapPanel Margin="24,8,0,4">
<TextBlock
Width="260"
Style="{StaticResource MaterialDesignBody1TextBlock}"
Text="Pop Out Panels" />
<StackPanel
Width="490"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Width="315"
VerticalAlignment="Center"
IsEnabled="False"
Style="{StaticResource TxtBlockDisableWhenLockedOrPanelSourceEdit}"
Text="Use legacy custom camera to select panel source" />
<ToggleButton
Width="50"
HorizontalAlignment="Left"
IsChecked="{Binding ActiveProfile.IsUsedLegacyCameraSystem, Mode=TwoWay, NotifyOnTargetUpdated=True}"
IsHitTestVisible="{c:Binding '!(ActiveProfile.IsEditingPanelSource or ActiveProfile.IsLocked)'}"
Style="{StaticResource ToggleButton}"
ToolTip="Switch panel selection method between using custom camera view or fixed camera view" />
</StackPanel>
<ToggleButton
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0,0,0,0"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=CrosshairsGps,
Size={StaticResource IconSize}}"
Background="Transparent"
Command="{Binding ToggleEditPanelSourceCommand}"
IsChecked="{Binding ActiveProfile.IsEditingPanelSource, Mode=TwoWay}"
IsEnabled="{c:Binding '!ActiveProfile.IsLocked'}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Toggle editing of panel source">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="Crosshairs" />
<ToggleButton.Style>
<Style BasedOn="{StaticResource MaterialDesignActionSecondaryToggleButton}" TargetType="ToggleButton">
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveProfile.IsEditingPanelSource}" Value="True">
<Setter Property="Foreground" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="8,0,0,0"
Command="{Binding AddPanelCommand}"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignIconForegroundButton}"
ToolTip="Add pop out panel">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="PlusThick" />
</Button>
<ToggleButton
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="8,0,0,0"
materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=LockOutline,
Size={StaticResource IconSize}}"
Background="Transparent"
Command="{Binding ToggleLockProfileCommand}"
IsChecked="{Binding ActiveProfile.IsLocked, Mode=TwoWay}"
KeyboardNavigation.AcceptsReturn="False"
ToolTip="Lock and unlock pop out panel settings">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="UnlockedOutline" />
<ToggleButton.Style>
<Style BasedOn="{StaticResource MaterialDesignActionSecondaryToggleButton}" TargetType="ToggleButton">
<Style.Triggers>
<DataTrigger Binding="{Binding ActiveProfile.IsLocked}" Value="True">
<Setter Property="Foreground" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</WrapPanel>
<appUserControl:PopOutPanelListEmpty Visibility="{c:Binding 'ActiveProfile.PanelConfigs.Count == 0'}" />
<appUserControl:PopOutPanelList Visibility="{c:Binding 'ActiveProfile.PanelConfigs.Count > 0'}" />
</StackPanel>
<StackPanel Grid.Row="3">
<Separator Margin="0,5,0,0" />
<Grid>
<Button
x:Name="BtnStartPopOut"
Width="Auto"
Margin="250,10,250,0"
Command="{Binding StartPopOutCommand}"
Content="Start Pop Out"
Foreground="White"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="Start pop out process" />
<Button
x:Name="BtnClosePopOut"
Width="Auto"
Margin="0,10,50,0"
HorizontalAlignment="Right"
Command="{Binding ClosePopOutCommand}"
Content="Close All Pop Outs"
Foreground="White"
KeyboardNavigation.AcceptsReturn="False"
Style="{StaticResource MaterialDesignOutlinedButton}"
ToolTip="Close all opened pop outs that are controlled by Popout Panel Manager" />
<StackPanel>
<materialDesign:Snackbar
x:Name="SnackbarMessage"
HorizontalAlignment="Stretch"
Background="DimGray"
Foreground="White"
IsActive="{Binding ActiveProfile.IsEditingPanelSource}">
<materialDesign:SnackbarMessage>
<StackPanel
Width="800"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock Margin="160,0,0,0" VerticalAlignment="Center">Please complete editing of source panel locations by clicking</TextBlock>
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="5,0,5,0"
Foreground="LightGreen"
Kind="CrosshairsGps" />
<TextBlock VerticalAlignment="Center">above to enable Start Pop Out</TextBlock>
</StackPanel>
</materialDesign:SnackbarMessage>
</materialDesign:Snackbar>
</StackPanel>
<StackPanel>
<materialDesign:Snackbar
x:Name="SnackbarMessage2"
HorizontalAlignment="Stretch"
Background="DimGray"
Foreground="White"
IsActive="{Binding ActiveProfile.HasUnidentifiedPanelSource}">
<materialDesign:SnackbarMessage>
<StackPanel
Width="800"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock Margin="250,0,0,0" VerticalAlignment="Center">Please identify all source panel locations to enable Start Pop Out</TextBlock>
</StackPanel>
</materialDesign:SnackbarMessage>
</materialDesign:Snackbar>
</StackPanel>
<StackPanel>
<materialDesign:Snackbar
x:Name="SnackbarMessage3"
HorizontalAlignment="Stretch"
Background="DimGray"
Foreground="White"
IsActive="{Binding ActiveProfile.IsDisabledStartPopOut}">
<materialDesign:SnackbarMessage>
<StackPanel
Width="800"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock Margin="250,0,0,0" VerticalAlignment="Center">Pop out in progress. Please wait and do not move your mouse.</TextBlock>
</StackPanel>
</materialDesign:SnackbarMessage>
</materialDesign:Snackbar>
</StackPanel>
</Grid>
</StackPanel>
</Grid>
</materialDesign:Card>
</Grid>
</UserControl>

View file

@ -0,0 +1,83 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class ProfileCard
{
private readonly ProfileCardViewModel _viewModel;
public ProfileCard()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<ProfileCardViewModel>();
Loaded += (_, _) => { DataContext = _viewModel; };
}
private void ToggleButtonEditProfileTitle_Click(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton { IsChecked: not null } toggleButton && (bool)toggleButton.IsChecked)
{
TxtBoxProfileTitle.Dispatcher.BeginInvoke(new Action(() => TxtBoxProfileTitle.Focus()));
TxtBoxProfileTitle.Dispatcher.BeginInvoke(new Action(() => TxtBoxProfileTitle.SelectAll()));
}
}
private void TxtBoxProfileTitle_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter)
return;
ToggleButtonEditProfileTitle.IsChecked = false;
Keyboard.ClearFocus();
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(RootCard), RootCard);
}
private void IncludeInGamePanel_TargetUpdated(object sender, DataTransferEventArgs e)
{
_viewModel.IncludeInGamePanelUpdatedCommand.Execute(null);
}
private void AddHudBar_TargetUpdated(object sender, DataTransferEventArgs e)
{
_viewModel.AddHudBarUpdatedCommand.Execute(null);
}
private void AddRefocusDisplay_TargetUpdated(object sender, DataTransferEventArgs e)
{
_viewModel.RefocusDisplayUpdatedCommand.Execute(null);
}
private void AddNumPad_TargetUpdated(object sender, DataTransferEventArgs e)
{
_viewModel.AddNumPadUpdatedCommand.Execute(null);
}
}
[ValueConversion(typeof(DateTime), typeof(String))]
public class StringToHudBarTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value?.ToString()?.Replace("_", " ");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var data = value.ToString();
return Enum.Parse<HudBarType>(data.Replace(" ", "_"));
}
}
}

View file

@ -0,0 +1,44 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.ProfileCardEmpty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="900"
Height="535"
mc:Ignorable="d">
<Grid d:DataContext="{d:DesignInstance viewmodel:ProfileCardViewModel}">
<materialDesign:Card
Width="900"
Height="535"
UniformCornerRadius="16">
<StackPanel>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock
Margin="0,16,5,0"
HorizontalAlignment="Center"
FontSize="16"
Foreground="gray">
Click here to start
</TextBlock>
<materialDesign:PackIcon
Width="40"
Height="40"
Margin="0,5,0,0"
HorizontalAlignment="Right"
Foreground="white"
Kind="ArrowRightBoldOutline" />
</StackPanel>
<TextBlock
Margin="0,210,0,0"
HorizontalAlignment="Center"
FontSize="24"
Foreground="gray">
Get started by adding a new aircraft profile
</TextBlock>
</StackPanel>
</materialDesign:Card>
</Grid>
</UserControl>

View file

@ -0,0 +1,10 @@
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class ProfileCardEmpty
{
public ProfileCardEmpty()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,167 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.ProfileCardList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dotNetKitControls="clr-namespace:DotNetKit.Windows.Controls;assembly=DotNetKit.Wpf.AutoCompleteComboBox"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Width="1024"
mc:Ignorable="d">
<UserControl.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
<DataTrigger
x:Key="TriggerIsDisabledAppInput"
Binding="{Binding ActiveProfile.IsDisabledStartPopOut}"
Value="True">
<d:DataTrigger.DataContext>
<x:Type Type="viewmodel:ProfileCardListViewModel" />
</d:DataTrigger.DataContext>
<Setter Property="FrameworkElement.Opacity" Value="0.5" />
<Setter Property="FrameworkElement.IsHitTestVisible" Value="False" />
</DataTrigger>
</UserControl.Resources>
<StackPanel d:DataContext="{d:DesignInstance viewmodel:ProfileCardListViewModel}" Orientation="Horizontal">
<StackPanel.Style>
<Style>
<Style.Triggers>
<StaticResource ResourceKey="TriggerIsDisabledAppInput" />
</Style.Triggers>
</Style>
</StackPanel.Style>
<DockPanel Width="62">
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="6,0,0,0"
Command="{Binding PreviousProfileCommand}"
IsEnabled="{c:Binding 'ProfileData.Profiles.Count > 1'}"
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
ToolTip="Previous aircraft profile">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="ChevronLeft" />
</Button>
</DockPanel>
<DockPanel Width="900">
<materialDesign:Transitioner x:Name="TransitionProfile" SelectedIndex="{Binding ProfileTransitionIndex}">
<materialDesign:TransitionerSlide x:Name="Slide1" ClipToBounds="True">
<materialDesign:TransitionerSlide.ForwardWipe>
<materialDesign:SlideWipe Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.ForwardWipe>
<materialDesign:TransitionerSlide.BackwardWipe>
<materialDesign:SlideWipe Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.BackwardWipe>
<materialDesign:TransitionerSlide.OpeningEffects>
<materialDesign:TransitionEffect Kind="SlideInFromRight" Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.OpeningEffects>
<StackPanel>
<appUserControl:ProfileCardEmpty Visibility="{c:Binding 'ProfileData.Profiles.Count == 0'}" />
<appUserControl:ProfileCard Visibility="{c:Binding 'ProfileData.Profiles.Count > 0'}" />
</StackPanel>
</materialDesign:TransitionerSlide>
<materialDesign:TransitionerSlide x:Name="Slide2" ClipToBounds="True">
<materialDesign:TransitionerSlide.ForwardWipe>
<materialDesign:SlideWipe Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.ForwardWipe>
<materialDesign:TransitionerSlide.BackwardWipe>
<materialDesign:SlideWipe Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.BackwardWipe>
<materialDesign:TransitionerSlide.OpeningEffects>
<materialDesign:TransitionEffect Kind="SlideInFromRight" Duration="0:0:0.8" />
</materialDesign:TransitionerSlide.OpeningEffects>
<StackPanel>
<appUserControl:ProfileCardEmpty Visibility="{c:Binding 'ProfileData.Profiles.Count == 0'}" />
<appUserControl:ProfileCard Visibility="{c:Binding 'ProfileData.Profiles.Count > 0'}" />
</StackPanel>
</materialDesign:TransitionerSlide>
</materialDesign:Transitioner>
</DockPanel>
<DockPanel Width="62" Margin="-5,10,0,0">
<StackPanel Orientation="Vertical">
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Command="{Binding AddProfileCommand}"
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
ToolTip="Add a new aircraft profile">
<materialDesign:PackIcon
Width="18"
Height="18"
Kind="FilePlusOutline" />
</Button>
<materialDesign:PopupBox
x:Name="PopupBoxFinder"
Margin="16,16,0,0"
materialDesign:ElevationAssist.Elevation="Dp0"
IsEnabled="{c:Binding 'ProfileData.Profiles.Count > 1'}"
PlacementMode="LeftAndAlignMiddles"
PopupHorizontalOffset="-10"
PopupUniformCornerRadius="4"
Style="{StaticResource MaterialDesignPopupBox}">
<materialDesign:PopupBox.ToggleContent>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0"
Padding="0"
Click="BtnPopupBoxFinder_Click"
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
ToolTip="Find an aircraft profile">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Foreground="White"
Kind="Magnify" />
</Button>
</materialDesign:PopupBox.ToggleContent>
<dotNetKitControls:AutoCompleteComboBox
x:Name="ComboBoxSearchProfile"
Width="250"
Margin="5,0,5,0"
Padding="5,0,5,0"
materialDesign:ComboBoxAssist.MaxLength="20"
materialDesign:HintAssist.Hint="Search Profile"
materialDesign:HintAssist.HintOpacity="0.3"
DisplayMemberPath="Name"
Foreground="White"
ItemsSource="{Binding ProfileData.Profiles}"
MaxDropDownHeight="400"
SelectedItem="{Binding SearchProfileSelectedItem}"
SelectedValuePath="Id"
Style="{StaticResource MaterialDesignComboBox}"
TextSearch.TextPath="Name">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SearchProfileSelectedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</dotNetKitControls:AutoCompleteComboBox>
</materialDesign:PopupBox>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0,170,0,0"
Command="{Binding NextProfileCommand}"
DockPanel.Dock="Top"
IsEnabled="{c:Binding 'ProfileData.Profiles.Count > 1'}"
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
ToolTip="Next aircraft profile">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="ChevronRight" />
</Button>
</StackPanel>
</DockPanel>
</StackPanel>
</UserControl>

View file

@ -0,0 +1,42 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using System.ComponentModel;
using System.Windows;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class ProfileCardList
{
private readonly ProfileCardListViewModel _viewModel;
public ProfileCardList()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<ProfileCardListViewModel>();
Loaded += (_, _) =>
{
DataContext = _viewModel;
_viewModel.OnProfileSelected += (_, _) =>
{
PopupBoxFinder.StaysOpen = false;
PopupBoxFinder.IsPopupOpen = false;
};
};
}
private void BtnPopupBoxFinder_Click(object sender, RoutedEventArgs e)
{
PopupBoxFinder.IsPopupOpen = !PopupBoxFinder.IsPopupOpen;
PopupBoxFinder.StaysOpen = PopupBoxFinder.IsPopupOpen;
if (PopupBoxFinder.IsPopupOpen)
{
ComboBoxSearchProfile.Text = null;
ComboBoxSearchProfile.Focus();
}
}
}
}

View file

@ -0,0 +1,55 @@
<UserControl
x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.TrayIcon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="MenuItem">
<Setter Property="FontSize" Value="14" />
</Style>
</UserControl.Resources>
<tb:TaskbarIcon
x:Name="Tray"
d:DataContext="{d:DesignInstance viewModel:TrayIconViewModel}"
IconSource="../logo.ico"
MenuActivation="RightClick"
Visibility="Visible">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem
x:Name="MenuItemProfiles"
Click="MenuItem_Click"
Header="Profiles"
ItemsSource="{Binding ProfileData.Profiles}"
Style="{StaticResource MaterialDesignMenuItem}">
<MenuItem.ItemContainerStyle>
<Style BasedOn="{StaticResource MaterialDesignMenuItem}" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding IsActive}" />
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=DataContext.ChangeProfileCommand}" />
<Setter Property="CommandParameter" Value="{Binding Id}" />
<Setter Property="FlowDirection" Value="LeftToRight" />
<Setter Property="Margin" Value="0" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem
Command="{Binding StartPopOutCommand}"
Header="Start Pop Out"
Style="{StaticResource MaterialDesignMenuItem}" />
<Separator />
<MenuItem
Command="{Binding ExitAppCommand}"
Header="Exit"
Style="{StaticResource MaterialDesignMenuItem}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</UserControl>

View file

@ -0,0 +1,35 @@
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using Prism.Commands;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace MSFSPopoutPanelManager.MainApp.AppUserControl
{
public partial class TrayIcon
{
// This command has to be here since it doesn't work in view model, window StateChanged never gets fire
public DelegateCommand RestoreWindowCommand => new(() => { ((Window)((Border)((Grid)this.Parent).Parent).Parent).WindowState = WindowState.Normal; }, () => true);
private TrayIconViewModel ViewModel { get; }
public TrayIcon()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
ViewModel = App.AppHost.Services.GetRequiredService<TrayIconViewModel>();
Loaded += (_, _) => { DataContext = ViewModel; };
Tray.DoubleClickCommand = RestoreWindowCommand;
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
if (Tray.ContextMenu != null)
Tray.ContextMenu.IsOpen = true;
}
}
}

View file

@ -0,0 +1,165 @@
<Window
x:Class="MSFSPopoutPanelManager.MainApp.AppWindow.AppMainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Title="MSFS POP OUT PANEL MANAGER"
Width="1000"
Height="600"
AllowsTransparency="True"
Background="Transparent"
Icon="../logo.ico"
ResizeMode="NoResize"
Style="{StaticResource MaterialDesignWindow}"
WindowState="{Binding InitialWindowState, Mode=OneWay}"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<system:Double x:Key="IconSize">28</system:Double>
</Window.Resources>
<Border
Margin="0"
Background="{StaticResource MaterialDesignDarkBackground}"
CornerRadius="15">
<Grid d:DataContext="{d:DesignInstance viewmodel:ApplicationViewModel}">
<appUserControl:TrayIcon x:Name="SystemTrayIcon" />
<materialDesign:DialogHost
Background="Transparent"
DialogTheme="Inherit"
Identifier="RootDialog">
<materialDesign:DrawerHost
OpenMode="Standard"
OverlayBackground="Transparent"
RightDrawerCornerRadius="15">
<materialDesign:DrawerHost.RightDrawerContent>
<DockPanel>
<Button
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="8,8,36,0"
HorizontalAlignment="Right"
Command="{x:Static materialDesign:DrawerHost.CloseDrawerCommand}"
DockPanel.Dock="Top"
Style="{StaticResource MaterialDesignIconButton}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="ArrowRight" />
</Button>
<StackPanel Name="PanelDrawers" />
</DockPanel>
</materialDesign:DrawerHost.RightDrawerContent>
<DockPanel>
<materialDesign:ColorZone
Padding="12,4,12,4"
materialDesign:ElevationAssist.Elevation="Dp4"
CornerRadius="15,15,0,0"
DockPanel.Dock="Top"
Mode="PrimaryDark">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,0,0">
<Button
Margin="0"
Padding="0"
HorizontalAlignment="Left"
IsHitTestVisible="false">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatButton}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.IsSimConnectDataReceived}" Value="True">
<Setter Property="Foreground" Value="LightGreen" />
<Setter Property="Content" Value="{materialDesign:PackIcon Kind=AccessPointNetwork, Size={StaticResource IconSize}}" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.IsSimConnectDataReceived}" Value="False">
<Setter Property="Foreground" Value="Red" />
<Setter Property="Content" Value="{materialDesign:PackIcon Kind=AccessPointNetworkOff, Size={StaticResource IconSize}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<StackPanel Grid.Column="1" Width="Auto">
<TextBlock
Margin="0,4,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Style="{StaticResource MaterialDesignHeadline6TextBlock}"
Text="MSFS POP OUT PANEL MANAGER" />
</StackPanel>
<StackPanel
Grid.Column="2"
Margin="0,0,0,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="0,0,0,0"
Click="SettingsButton_Click"
Command="{x:Static materialDesign:DrawerHost.OpenDrawerCommand}"
Foreground="White"
Style="{StaticResource MaterialDesignIconButton}"
ToolTip="Preferences">
<materialDesign:PackIcon Kind="Cog" />
</Button>
<Button
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="10,0,0,0"
Click="HelpButton_Click"
Command="{x:Static materialDesign:DrawerHost.OpenDrawerCommand}"
Foreground="White"
Style="{StaticResource MaterialDesignIconButton}"
ToolTip="Help">
<materialDesign:PackIcon Kind="Help" />
</Button>
<Button
x:Name="BtnMinimize"
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="10,0,0,0"
Click="BtnMinimize_Click"
Foreground="White"
Style="{StaticResource MaterialDesignIconButton}"
ToolTip="Minimize application">
<materialDesign:PackIcon Kind="WindowMinimize" />
</Button>
<Button
x:Name="BtnClose"
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="10,0,0,0"
Click="BtnClose_Click"
Foreground="White"
Style="{StaticResource MaterialDesignIconButton}"
ToolTip="Close application">
<materialDesign:PackIcon Kind="Close" />
</Button>
</StackPanel>
</Grid>
</materialDesign:ColorZone>
<appUserControl:ProfileCardList
x:Name="ProfileCardList"
Width="1024"
Margin="0,8,0,8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
DockPanel.Dock="Top" />
</DockPanel>
</materialDesign:DrawerHost>
</materialDesign:DialogHost>
</Grid>
</Border>
</Window>

View file

@ -0,0 +1,102 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Interop;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.AppUserControl;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using MSFSPopoutPanelManager.WindowsAgent;
namespace MSFSPopoutPanelManager.MainApp.AppWindow
{
public partial class AppMainWindow
{
private readonly ApplicationViewModel _viewModel;
public AppMainWindow()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<ApplicationViewModel>();
Loaded += AppWindow_Loaded;
Closing += AppWindow_Closing;
StateChanged += AppWindow_StateChanged;
WindowActionManager.OnPopOutManagerAlwaysOnTopChanged += (_, e) => { Topmost = e; };
MouseLeftButtonDown += (_, _) => DragMove();
}
private void AppWindow_Loaded(object sender, RoutedEventArgs e)
{
var window = Window.GetWindow(this);
if (window == null)
throw new ApplicationException("Cannot instantiate Pop Out Panel Manager.");
_viewModel.ApplicationHandle = new WindowInteropHelper(window).Handle;
_viewModel.ApplicationWindow = Application.Current.MainWindow;
_viewModel.Initialize();
DataContext = _viewModel;
// Try to fix always on to click through. This won't work for application's title bar since mouseEnter won't be triggered.
// This is super tricky to trick POPM process is MSFS process to avoid Windows OS focus stealing
MouseEnter += (_, _) =>WindowActionManager.SetWindowFocus(WindowProcessManager.GetApplicationProcess().Handle);
}
private void AppWindow_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
_viewModel.WindowClosing();
}
private void AppWindow_StateChanged(object sender, EventArgs e)
{
switch (WindowState)
{
case WindowState.Maximized:
ShowInTaskbar = true;
break;
case WindowState.Minimized:
if (_viewModel.AppSettingData.ApplicationSetting.GeneralSetting.MinimizeToTray)
{
SystemTrayIcon.Tray.Visibility = Visibility.Visible;
ShowInTaskbar = false;
}
break;
case WindowState.Normal:
SystemTrayIcon.Tray.Visibility = Visibility.Hidden;
ShowInTaskbar = true;
// Fix always on top status once app is minimize and then restore
if (_viewModel.AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop)
WindowActionManager.ApplyAlwaysOnTop(_viewModel.ApplicationHandle, PanelType.PopOutManager, true);
break;
}
}
private void SettingsButton_Click(object sender, RoutedEventArgs e)
{
PanelDrawers.Children.Clear();
PanelDrawers.Children.Add(new PreferenceDrawer());
}
private void HelpButton_Click(object sender, RoutedEventArgs e)
{
PanelDrawers.Children.Clear();
PanelDrawers.Children.Add(new HelpDrawer());
}
private void BtnMinimize_Click(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
}

View file

@ -0,0 +1,284 @@
<Window
x:Class="MSFSPopoutPanelManager.MainApp.AppWindow.HudBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Title="HudBar"
Width="1920"
MinWidth="1920"
MinHeight="40"
MaxHeight="40"
ResizeMode="NoResize"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<system:Double x:Key="IconSize">22</system:Double>
<system:Double x:Key="ButtonSize">28</system:Double>
<Style
x:Key="TxtBoxTitle"
BasedOn="{StaticResource MaterialDesignBody1TextBlock}"
TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10,5,10,5" />
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.IsSimConnectActive}" Value="False">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="TxtBoxLabel"
BasedOn="{StaticResource MaterialDesignBody1TextBlock}"
TargetType="TextBlock">
<Setter Property="Margin" Value="10,5,10,5" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style
x:Key="TxtBoxData"
BasedOn="{StaticResource MaterialDesignBody1TextBlock}"
TargetType="TextBlock">
<Setter Property="Margin" Value="0,5,10,5" />
<Setter Property="Foreground" Value="Lime" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style
x:Key="VerticalSeparator"
BasedOn="{StaticResource {x:Type Separator}}"
TargetType="Separator">
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="6,0,6,0" />
<Setter Property="LayoutTransform">
<Setter.Value>
<TransformGroup>
<TransformGroup.Children>
<TransformCollection>
<RotateTransform Angle="90" />
</TransformCollection>
</TransformGroup.Children>
</TransformGroup>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<materialDesign:ColorZone Height="40" Mode="Dark">
<Grid d:DataContext="{d:DesignInstance viewmodel:HudBarViewModel}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="130" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="10,5,0,0"
Style="{StaticResource TxtBoxTitle}"
Text="{Binding HudBarTypeText}" />
<DockPanel Grid.Column="1">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="2">
<TextBlock Style="{StaticResource TxtBoxLabel}">Elevator Trim:</TextBlock>
<TextBlock Style="{StaticResource TxtBoxData}" Text="{Binding FlightSimData.HudBarData.ElevatorTrimFormatted}" />
</DockPanel>
<DockPanel Grid.Column="3">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="4">
<TextBlock Style="{StaticResource TxtBoxLabel}">Aileron Trim:</TextBlock>
<TextBlock Style="{StaticResource TxtBoxData}" Text="{Binding FlightSimData.HudBarData.AileronTrimFormatted}" />
</DockPanel>
<DockPanel Grid.Column="5">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="6">
<TextBlock Style="{StaticResource TxtBoxLabel}">Rudder Trim:</TextBlock>
<TextBlock Style="{StaticResource TxtBoxData}" Text="{Binding FlightSimData.HudBarData.RudderTrimFormatted}" />
</DockPanel>
<DockPanel Grid.Column="7">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="8">
<TextBlock Style="{StaticResource TxtBoxLabel}">Flaps:</TextBlock>
<TextBlock Style="{StaticResource TxtBoxData}" Text="{Binding FlightSimData.HudBarData.FlapFormatted}" />
</DockPanel>
<DockPanel Grid.Column="9">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="10">
<TextBlock Style="{StaticResource TxtBoxLabel}">Brake:</TextBlock>
<TextBlock Text="{Binding FlightSimData.HudBarData.ParkingBrakeFormatted}">
<TextBlock.Style>
<Style BasedOn="{StaticResource TxtBoxData}" TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.ParkingBrakeFormatted}" Value="Disengaged">
<Setter Property="TextBlock.Foreground" Value="DimGray" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.ParkingBrakeFormatted}" Value="Engaged">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DockPanel>
<DockPanel Grid.Column="11">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="12">
<TextBlock Style="{StaticResource TxtBoxLabel}">Gear:</TextBlock>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Text="L">
<TextBlock.Style>
<Style BasedOn="{StaticResource TxtBoxData}" TargetType="TextBlock">
<Setter Property="Margin" Value="10,5,10,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearLeftFormatted}" Value="DOWN">
<Setter Property="TextBlock.Foreground" Value="Lime" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearLeftFormatted}" Value="UP">
<Setter Property="TextBlock.Foreground" Value="DimGray" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearLeftFormatted}" Value="MOVING">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="C">
<TextBlock.Style>
<Style BasedOn="{StaticResource TxtBoxData}" TargetType="TextBlock">
<Setter Property="Margin" Value="10,5,10,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearCenterFormatted}" Value="DOWN">
<Setter Property="TextBlock.Foreground" Value="Lime" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearCenterFormatted}" Value="UP">
<Setter Property="TextBlock.Foreground" Value="DimGray" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearCenterFormatted}" Value="MOVING">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="R">
<TextBlock.Style>
<Style BasedOn="{StaticResource TxtBoxData}" TargetType="TextBlock">
<Setter Property="Margin" Value="10,5,10,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearRightFormatted}" Value="DOWN">
<Setter Property="TextBlock.Foreground" Value="Lime" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearRightFormatted}" Value="UP">
<Setter Property="TextBlock.Foreground" Value="DimGray" />
</DataTrigger>
<DataTrigger Binding="{Binding FlightSimData.HudBarData.GearRightFormatted}" Value="MOVING">
<Setter Property="TextBlock.Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DockPanel>
<DockPanel Grid.Column="13">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="14">
<TextBlock Style="{StaticResource TxtBoxLabel}">Timer:</TextBlock>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Style="{StaticResource TxtBoxData}" Text="{Binding Timer, StringFormat={}{0:HH:mm:ss}}" />
<Button
Width="80"
Height="28"
Margin="10,0,0,0"
Command="{Binding StartStopTimerCommand}"
Style="{StaticResource MaterialDesignOutlinedButton}">
<TextBlock Foreground="White" Text="{c:Binding 'IsTimerStarted ? &quot;STOP&quot; : &quot;START&quot;'}" />
</Button>
<Button
Width="80"
Height="28"
Margin="15,0,10,0"
Command="{Binding ResetTimerCommand}"
Style="{StaticResource MaterialDesignOutlinedButton}">
<TextBlock Foreground="White" Text="RESET" />
</Button>
</StackPanel>
</DockPanel>
<DockPanel Grid.Column="15">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="16">
<TextBlock Style="{StaticResource TxtBoxLabel}">Sim Rate:</TextBlock>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock
Margin="0,5,5,0"
Style="{StaticResource TxtBoxData}"
Text="{Binding FlightSimData.HudBarData.SimRate}" />
<TextBlock
Margin="0,5,20,0"
Style="{StaticResource TxtBoxData}"
Text="X" />
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0,0,15,0"
Command="{Binding IncreaseSimRateCommand}"
Style="{StaticResource MaterialDesignFloatingActionButton}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="ArrowUp" />
</Button>
<Button
Width="{StaticResource ButtonSize}"
Height="{StaticResource ButtonSize}"
Margin="0,0,10,0"
Command="{Binding DecreaseSimRateCommand}"
Style="{StaticResource MaterialDesignFloatingActionButton}">
<materialDesign:PackIcon
Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Kind="ArrowDown" />
</Button>
</StackPanel>
</DockPanel>
<DockPanel Grid.Column="17">
<Separator Style="{StaticResource VerticalSeparator}" />
</DockPanel>
<DockPanel Grid.Column="18">
<Button
x:Name="BtnClose"
Width="80"
Height="28"
Click="BtnClose_Click"
Style="{StaticResource MaterialDesignOutlinedButton}">
<TextBlock Foreground="White" Text="Close" />
</Button>
</DockPanel>
</Grid>
</materialDesign:ColorZone>
</Window>

View file

@ -0,0 +1,57 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppWindow
{
public partial class HudBar
{
private readonly HudBarViewModel _viewModel;
public HudBar(Guid panelId)
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<HudBarViewModel>();
_viewModel.PanelId = panelId;
Loaded += (_, _) =>
{
DataContext = _viewModel;
var window = Window.GetWindow(this);
if (window == null)
throw new ApplicationException("Unable to instantiate HudBar window");
_viewModel.PanelConfig.PanelHandle = new WindowInteropHelper(window).Handle;
_viewModel.PanelConfig.Width = Convert.ToInt32(Width);
_viewModel.PanelConfig.Height = Convert.ToInt32(Height);
};
this.MouseLeftButtonDown += HudBar_MouseLeftButtonDown;
this.Topmost = true;
Closing += HudBar_Closing;
}
private void HudBar_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_viewModel.CloseCommand.Execute(null);
}
private void HudBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

View file

@ -0,0 +1,53 @@
<Window
x:Class="MSFSPopoutPanelManager.MainApp.AppWindow.MessageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Title="MSFS Pop Out Panel Manager"
Width="Auto"
Height="Auto"
AllowsTransparency="True"
ResizeMode="NoResize"
ShowInTaskbar="False"
SizeToContent="WidthAndHeight"
Topmost="True"
WindowStyle="None"
mc:Ignorable="d">
<materialDesign:ColorZone
Width="Auto"
Height="Auto"
Mode="Dark">
<DockPanel
Width="{Binding WindowWidth}"
Height="{Binding WindowHeight}"
d:DataContext="{d:DesignInstance viewmodel:MessageWindowViewModel}">
<WrapPanel
Height="30"
Background="SlateGray"
DockPanel.Dock="Top">
<TextBlock
Padding="10,5,10,5"
HorizontalAlignment="Stretch"
Style="{StaticResource MaterialDesignBody1TextBlock}">
MSFS POP OUT PANEL MANAGER
</TextBlock>
</WrapPanel>
<WrapPanel Margin="20,5,20,10">
<ScrollViewer
x:Name="ScrollViewerMessage"
Height="Auto"
DockPanel.Dock="Bottom"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<TextBlock
x:Name="TextBlockMessage"
LineHeight="18"
LineStackingStrategy="BlockLineHeight" />
</ScrollViewer>
</WrapPanel>
</DockPanel>
</materialDesign:ColorZone>
</Window>

View file

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppWindow
{
public partial class MessageWindow
{
private readonly MessageWindowViewModel _viewModel;
public MessageWindow()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<MessageWindowViewModel>();
Loaded += (_, _) =>
{
DataContext = _viewModel;
var window = Window.GetWindow(this);
if (window == null)
throw new ApplicationException("Unable to instantiate status message window");
_viewModel.Handle = new WindowInteropHelper(window).Handle;
// Set window binding, needs to be in code after window loaded
var visibleBinding = new Binding("IsVisible")
{
Source = _viewModel,
Converter = new BooleanToVisibilityConverter()
};
BindingOperations.SetBinding(this, Window.VisibilityProperty, visibleBinding);
// Set window click through
WindowsServices.SetWindowExTransparent(_viewModel.Handle);
_viewModel.OnMessageUpdated += ViewModel_OnMessageUpdated;
};
}
private void ViewModel_OnMessageUpdated(object sender, List<Run> e)
{
if (e == null)
return;
TextBlockMessage.Inlines.Clear();
foreach (var run in e)
TextBlockMessage.Inlines.Add(run);
ScrollViewerMessage.ScrollToEnd();
}
}
public static class WindowsServices
{
const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int index, int newStyle);
public static void SetWindowExTransparent(IntPtr hWnd)
{
var extendedStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
SetWindowLong(hWnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
}
}

View file

@ -0,0 +1,163 @@
<Window
x:Class="MSFSPopoutPanelManager.MainApp.AppWindow.NumPad"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Title="Virtual NumPad"
Width="300"
Height="400"
ResizeMode="CanResize"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="Transparent" />
<EventSetter Event="Click" Handler="Button_Click" />
</Style>
</Window.Resources>
<materialDesign:ColorZone
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Mode="Dark">
<Grid d:DataContext="{d:DesignInstance viewModel:NumPadViewModel}">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="10,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="White">
Virtual NumPad
</TextBlock>
<Button
x:Name="BtnClose"
Grid.Row="0"
Grid.Column="3"
Width="28"
Height="28"
Margin="5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="BtnClose_Click"
Foreground="White"
Style="{StaticResource MaterialDesignIconButton}"
ToolTip="Close NumPad">
<materialDesign:PackIcon Kind="Close" />
</Button>
<Button
x:Name="BtnTab"
Grid.Row="1"
Grid.Column="0"
Content="Tab" />
<Button
x:Name="BtnDivide"
Grid.Row="1"
Grid.Column="1"
Content="/" />
<Button
x:Name="BtnMultiply"
Grid.Row="1"
Grid.Column="2"
Padding="0,8,0,0"
Content="*" />
<Button
x:Name="BtnSubtract"
Grid.Row="1"
Grid.Column="3"
Content="-" />
<Button
x:Name="Btn7"
Grid.Row="2"
Grid.Column="0"
Content="7" />
<Button
x:Name="Btn8"
Grid.Row="2"
Grid.Column="1"
Content="8" />
<Button
x:Name="Btn9"
Grid.Row="2"
Grid.Column="2"
Content="9" />
<Button
x:Name="BtnAdd"
Grid.Row="2"
Grid.RowSpan="2"
Grid.Column="3"
Content="+" />
<Button
x:Name="Btn4"
Grid.Row="3"
Grid.Column="0"
Content="4" />
<Button
x:Name="Btn5"
Grid.Row="3"
Grid.Column="1"
Content="5" />
<Button
x:Name="Btn6"
Grid.Row="3"
Grid.Column="2"
Content="6" />
<Button
x:Name="Btn1"
Grid.Row="4"
Grid.Column="0"
Content="1" />
<Button
x:Name="Btn2"
Grid.Row="4"
Grid.Column="1"
Content="2" />
<Button
x:Name="Btn3"
Grid.Row="4"
Grid.Column="2"
Content="3" />
<Button
x:Name="BtnEnter"
Grid.Row="4"
Grid.RowSpan="2"
Grid.Column="3"
Content="Ent" />
<Button
x:Name="Btn0"
Grid.Row="5"
Grid.Column="0"
Grid.ColumnSpan="2"
Content="0" />
<Button
x:Name="BtnDecimal"
Grid.Row="5"
Grid.Column="2"
Content="." />
</Grid>
</materialDesign:ColorZone>
</Window>

View file

@ -0,0 +1,57 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.MainApp.ViewModel;
namespace MSFSPopoutPanelManager.MainApp.AppWindow
{
public partial class NumPad
{
private readonly NumPadViewModel _viewModel;
public NumPad(Guid panelId)
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return;
_viewModel = App.AppHost.Services.GetRequiredService<NumPadViewModel>();
_viewModel.PanelId = panelId;
Loaded += (_, _) =>
{
DataContext = _viewModel;
var window = Window.GetWindow(this);
if (window == null)
throw new ApplicationException("Unable to instantiate NumPad window");
_viewModel.PanelConfig.PanelHandle = new WindowInteropHelper(window).Handle;
_viewModel.PanelConfig.Width = Convert.ToInt32(Width);
_viewModel.PanelConfig.Height = Convert.ToInt32(Height);
};
this.MouseLeftButtonDown += NumPad_MouseLeftButtonDown;
this.Topmost = true;
}
private void NumPad_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (e.Source is Button btn)
_viewModel.ButtonCommand.Execute(btn.Name.Substring(3));
}
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

View file

@ -0,0 +1,66 @@
<Window
x:Class="MSFSPopoutPanelManager.MainApp.AppWindow.PanelCoorOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel"
Title="PanelCoorOverlay"
Width="30"
Height="30"
AllowsTransparency="True"
Background="#01F0F0FF"
Loaded="Window_Loaded"
MouseMove="Window_MouseMove"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStyle="None"
mc:Ignorable="d">
<Canvas
Width="30"
Height="30"
d:DataContext="{d:DesignInstance viewModel:PanelCoorOverlayViewModel}"
PreviewMouseDown="Canvas_PreviewMouseDown"
ToolTip="{Binding Panel.PanelName}">
<Grid>
<Ellipse
x:Name="OverlayCircle"
Width="28"
Height="28"
Fill="Transparent"
StrokeThickness="6" />
<Ellipse
x:Name="OverlayBlinkingCircle"
Width="30"
Height="30"
Fill="Transparent"
Stroke="Black"
StrokeThickness="7"
Visibility="{c:Binding Panel.IsShownPanelSource}">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.4" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
<Ellipse
x:Name="OverlayCircleBorder"
Width="30"
Height="30"
Fill="Transparent"
Opacity="0.5"
Stroke="White" />
</Grid>
</Canvas>
</Window>

View file

@ -0,0 +1,110 @@
using System;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using Microsoft.Extensions.DependencyInjection;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.MainApp.ViewModel;
using MSFSPopoutPanelManager.WindowsAgent;
namespace MSFSPopoutPanelManager.MainApp.AppWindow
{
public partial class PanelCoorOverlay
{
private readonly PanelCoorOverlayViewModel _viewModel;
private const int WINDOW_ADJUSTMENT = 20; // half of window height with shadow adjustment
private int _xCoor;
private int _yCoor;
public bool IsEditingPanelLocation { get; set; }
public Guid PanelId { get; set; }
public bool IsAllowedEdit { get; set; }
public event EventHandler<System.Drawing.Point> OnWindowLocationChanged;
public PanelCoorOverlay(Guid id, bool isAllowedEdit)
{
_viewModel = App.AppHost.Services.GetRequiredService<PanelCoorOverlayViewModel>();
_viewModel.SetPanelId(id);
PanelId = id;
IsAllowedEdit = isAllowedEdit;
InitializeComponent();
Loaded += PanelCoorOverlay_Loaded;
var color = ColorConverter.ConvertFromString(_viewModel.Panel.PanelSource.Color);
OverlayCircle.Stroke = color == null ? Brushes.White : new SolidColorBrush((Color) color);
if (!_viewModel.ActiveProfile.IsEditingPanelSource)
OverlayBlinkingCircle.Visibility = Visibility.Collapsed;
IsEditingPanelLocation = false;
this.Topmost = true;
this.Left = 0;
this.Top = 0;
this.MouseUp += PanelCoorOverlay_MouseUp; // detect location change when user release mouse button when dragging the overlay window
this.Background = isAllowedEdit ? new SolidColorBrush(Color.FromArgb(1, 240, 240, 255)) : new SolidColorBrush(System.Windows.Media.Colors.Transparent);
}
private void PanelCoorOverlay_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = _viewModel;
}
private void PanelCoorOverlay_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!IsAllowedEdit)
return;
if (this.Top is double.NaN || this.Left is double.NaN)
return;
// Fixed broken window left/top coordinate for DPI Awareness Per Monitor
var handle = new WindowInteropHelper(this).Handle;
var rect = WindowActionManager.GetWindowRectangle(handle);
OnWindowLocationChanged?.Invoke(this, new System.Drawing.Point(rect.X + WINDOW_ADJUSTMENT, rect.Y + WINDOW_ADJUSTMENT));
if (_viewModel.Panel != null)
_viewModel.Panel.IsSelectedPanelSource = false;
}
public void SetWindowCoor(int x, int y)
{
_xCoor = x;
_yCoor = y;
}
private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (!IsAllowedEdit)
return;
if (IsEditingPanelLocation && e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
this.DragMove();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Fixed broken window left/top coordinate for DPI Awareness Per Monitor
var handle = new WindowInteropHelper(this).Handle;
WindowActionManager.MoveWindow(handle, _xCoor - WINDOW_ADJUSTMENT, _yCoor - WINDOW_ADJUSTMENT, Convert.ToInt32(this.Width), Convert.ToInt32(this.Height));
WindowActionManager.ApplyAlwaysOnTop(handle, PanelType.PanelSourceWindow, true);
}
private void Canvas_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!IsAllowedEdit)
return;
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed && _viewModel.Panel != null)
_viewModel.Panel.IsSelectedPanelSource = true;
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
namespace MSFSPopoutPanelManager.MainApp.Converter
{
public class ExpanderRotateAngleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var factor = 1.0;
if (parameter is { } parameterValue)
{
if (!double.TryParse(parameterValue.ToString(), out factor))
{
factor = 1.0;
}
}
return value switch
{
ExpandDirection.Left => 90 * factor,
ExpandDirection.Right => -90 * factor,
_ => 0
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,22 +1,23 @@
using System; using System;
using System.Windows.Data; using System.Windows.Data;
namespace MSFSPopoutPanelManager.MainApp namespace MSFSPopoutPanelManager.MainApp.Converter
{ {
public class InverseBooleanOrConverter : IMultiValueConverter public class InverseBooleanOrConverter : IMultiValueConverter
{ {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ {
if (values.LongLength > 0) if (values.LongLength <= 0)
return true;
foreach (var value in values)
{ {
foreach (var value in values) if (value is true)
{ {
if (value is bool && (bool)value) return false;
{
return false;
}
} }
} }
return true; return true;
} }

View file

@ -0,0 +1,20 @@
using System;
using System.Windows.Data;
namespace MSFSPopoutPanelManager.MainApp.Converter
{
public class StringEmptyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ?? parameter;
}
public object ConvertBack(
object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View file

@ -8,8 +8,12 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
double v = (double)value; if (value == null)
return 0.0;
var v = (double)value;
return v * 0.6; return v * 0.6;
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

View file

@ -18,7 +18,7 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
{ {
base.OnApplyTemplate(); base.OnApplyTemplate();
TextBox? textBox = GetTemplateChild("PART_TextBox") as TextBox; TextBox textBox = GetTemplateChild("PART_TextBox") as TextBox;
if (textBox != null) if (textBox != null)
{ {
PART_TextBox = textBox; PART_TextBox = textBox;
@ -27,13 +27,13 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
PART_TextBox.Text = Value.ToString(); PART_TextBox.Text = Value.ToString();
} }
ButtonBase? PART_ButtonUp = GetTemplateChild("PART_ButtonUp") as ButtonBase; ButtonBase PART_ButtonUp = GetTemplateChild("PART_ButtonUp") as ButtonBase;
if (PART_ButtonUp != null) if (PART_ButtonUp != null)
{ {
PART_ButtonUp.Click += buttonUp_Click; PART_ButtonUp.Click += buttonUp_Click;
} }
ButtonBase? PART_ButtonDown = GetTemplateChild("PART_ButtonDown") as ButtonBase; ButtonBase PART_ButtonDown = GetTemplateChild("PART_ButtonDown") as ButtonBase;
if (PART_ButtonDown != null) if (PART_ButtonDown != null)
{ {
PART_ButtonDown.Click += buttonDown_Click; PART_ButtonDown.Click += buttonDown_Click;
@ -62,16 +62,16 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
public int Places public int Places
{ {
get { return (int)GetValue(PlacesProperty); } get => (int)GetValue(PlacesProperty);
set { SetValue(PlacesProperty, value); } set => SetValue(PlacesProperty, value);
} }
public static readonly DependencyProperty PlacesProperty = DependencyProperty.Register("Places", typeof(int), typeof(NumericUpDown)); public static readonly DependencyProperty PlacesProperty = DependencyProperty.Register("Places", typeof(int), typeof(NumericUpDown));
public double MaxValue public double MaxValue
{ {
get { return (double)GetValue(MaxValueProperty); } get => (double)GetValue(MaxValueProperty);
set { SetValue(MaxValueProperty, value); } set => SetValue(MaxValueProperty, value);
} }
public static readonly DependencyProperty MaxValueProperty = public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(100D, maxValueChangedCallback, coerceMaxValueCallback)); DependencyProperty.Register("MaxValue", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(100D, maxValueChangedCallback, coerceMaxValueCallback));
@ -92,8 +92,8 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
public double MinValue public double MinValue
{ {
get { return (double)GetValue(MinValueProperty); } get => (double)GetValue(MinValueProperty);
set { SetValue(MinValueProperty, value); } set => SetValue(MinValueProperty, value);
} }
public static readonly DependencyProperty MinValueProperty = public static readonly DependencyProperty MinValueProperty =
@ -117,11 +117,13 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
public double Increment public double Increment
{ {
get { return (double)GetValue(IncrementProperty); } get => (double)GetValue(IncrementProperty);
set { SetValue(IncrementProperty, value); } set => SetValue(IncrementProperty, value);
} }
public static readonly DependencyProperty IncrementProperty = public static readonly DependencyProperty IncrementProperty =
DependencyProperty.Register("Increment", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(1D, null, coerceIncrementCallback)); DependencyProperty.Register(nameof(Increment), typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(1D, null, coerceIncrementCallback));
private static object coerceIncrementCallback(DependencyObject d, object value) private static object coerceIncrementCallback(DependencyObject d, object value)
{ {
NumericUpDown numericUpDown = ((NumericUpDown)d); NumericUpDown numericUpDown = ((NumericUpDown)d);
@ -134,11 +136,13 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
public double Value public double Value
{ {
get { return (double)GetValue(ValueProperty); } get => (double)GetValue(ValueProperty);
set { SetValue(ValueProperty, value); } set => SetValue(ValueProperty, value);
} }
public static readonly DependencyProperty ValueProperty = public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(0D, valueChangedCallback, coerceValueCallback), validateValueCallback); DependencyProperty.Register(nameof(Value), typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(0D, valueChangedCallback, coerceValueCallback), validateValueCallback);
private static void valueChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void valueChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
NumericUpDown numericUpDown = (NumericUpDown)d; NumericUpDown numericUpDown = (NumericUpDown)d;
@ -147,6 +151,7 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
numericUpDown.RaiseEvent(ea); numericUpDown.RaiseEvent(ea);
numericUpDown.PART_TextBox.Text = e.NewValue.ToString(); numericUpDown.PART_TextBox.Text = e.NewValue.ToString();
} }
private static bool validateValueCallback(object value) private static bool validateValueCallback(object value)
{ {
double val = (double)value; double val = (double)value;
@ -155,6 +160,7 @@ namespace MSFSPopoutPanelManager.MainApp.CustomControl
else else
return false; return false;
} }
private static object coerceValueCallback(DependencyObject d, object value) private static object coerceValueCallback(DependencyObject d, object value)
{ {
double val = (double)value; double val = (double)value;

View file

@ -14,9 +14,9 @@
<RootNamespace>MSFSPopoutPanelManager.MainApp</RootNamespace> <RootNamespace>MSFSPopoutPanelManager.MainApp</RootNamespace>
<ApplicationIcon>logo.ico</ApplicationIcon> <ApplicationIcon>logo.ico</ApplicationIcon>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<Version>4.0.3.4</Version> <Version>4.0.4.1</Version>
<AssemblyVersion>4.0.3.4</AssemblyVersion> <AssemblyVersion>4.0.4.1</AssemblyVersion>
<FileVersion>4.0.3.4</FileVersion> <FileVersion>4.0.4.1</FileVersion>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<!-- Publishing options --> <!-- Publishing options -->
@ -63,7 +63,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="AppWindow.xaml.cs"> <Compile Update="AppWindow\AppMainWindow.xaml.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Update="Properties\Settings.Designer.cs"> <Compile Update="Properties\Settings.Designer.cs">

View file

@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
--> -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Debug</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
<PublishDir>..\..\..\publish\master</PublishDir> <PublishDir>..\..\..\publish\master</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>

View file

@ -2,8 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding" xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp" xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
xmlns:profileDomain="clr-namespace:MSFSPopoutPanelManager.DomainModel.Profile;assembly=DomainModel"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"> xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf">
<Duration x:Key="ExpandDuration">0:0:0.250</Duration> <Duration x:Key="ExpandDuration">0:0:0.250</Duration>
@ -26,7 +25,7 @@
Kind="ChevronDown" Kind="ChevronDown"
Opacity="0.5" Opacity="0.5"
RenderTransformOrigin="0.5 0.5" RenderTransformOrigin="0.5 0.5"
Visibility="{c:Binding 'DataItem.PanelType!=profileDomain:PanelType.HudBarWindow and DataItem.PanelType!=profileDomain:PanelType.RefocusDisplay'}"> Visibility="{c:Binding '!DataItem.IsHudBarWindow and !DataItem.IsRefocusDisplay and !DataItem.IsNumPadWindow and !ActiveProfile.IsEditingPanelSource'}">
<wpf:PackIcon.RenderTransform> <wpf:PackIcon.RenderTransform>
<RotateTransform x:Name="ExpandPathRotateTransform" /> <RotateTransform x:Name="ExpandPathRotateTransform" />
</wpf:PackIcon.RenderTransform> </wpf:PackIcon.RenderTransform>
@ -125,7 +124,7 @@
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}"> <ControlTemplate TargetType="{x:Type Expander}">
<ControlTemplate.Resources> <ControlTemplate.Resources>
<local:ExpanderRotateAngleConverter x:Key="ExpanderRotateAngleConverter" /> <converter:ExpanderRotateAngleConverter x:Key="ExpanderRotateAngleConverter" />
</ControlTemplate.Resources> </ControlTemplate.Resources>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel Background="{TemplateBinding Background}"> <DockPanel Background="{TemplateBinding Background}">

View file

@ -10,36 +10,42 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
public class AddProfileViewModel : BaseViewModel public class AddProfileViewModel : BaseViewModel
{ {
private readonly ProfileOrchestrator _profileOrchestrator;
private readonly PanelSourceOrchestrator _panelSourceOrchestrator;
public UserProfile Profile { get; set; } public UserProfile Profile { get; set; }
public UserProfile CopiedProfile { get; set; } public UserProfile CopiedProfile { get; set; }
public AddProfileViewModel(MainOrchestrator orchestrator) : base(orchestrator) public AddProfileViewModel(SharedStorage sharedStorage, ProfileOrchestrator profileOrchestrator, PanelSourceOrchestrator panelSourceOrchestrator) : base(sharedStorage)
{ {
_profileOrchestrator = profileOrchestrator;
_panelSourceOrchestrator = panelSourceOrchestrator;
Profile = new UserProfile(); Profile = new UserProfile();
} }
public void ClosingEventHandler(object sender, DialogClosingEventArgs eventArgs) public void ClosingEventHandler(object sender, DialogClosingEventArgs eventArgs)
{ {
if (eventArgs.Parameter != null && eventArgs.Parameter.Equals("ADD")) if (eventArgs.Parameter == null || !eventArgs.Parameter.Equals("ADD"))
return;
if (string.IsNullOrEmpty(Profile.Name.Trim()))
{ {
if (string.IsNullOrEmpty(Profile.Name.Trim())) Profile.Name = null;
{ eventArgs.Cancel();
Profile.Name = null; return;
eventArgs.Cancel();
return;
}
// Unload the current profile
ProfileData.ResetActiveProfile();
Orchestrator.PanelSource.CloseAllPanelSource();
Orchestrator.Profile.AddProfile(Profile.Name, CopiedProfile);
} }
// Unload the current profile
ProfileData.ResetActiveProfile();
_panelSourceOrchestrator.CloseAllPanelSource();
_profileOrchestrator.AddProfile(Profile.Name, CopiedProfile);
} }
} }
public class PostiveValidationRule : ValidationRule public class ProfileNameValidationRule : ValidationRule
{ {
public override ValidationResult Validate(object value, CultureInfo cultureInfo) => ValidationResult.ValidResult; // not used public override ValidationResult Validate(object value, CultureInfo cultureInfo) => ValidationResult.ValidResult; // not used
@ -47,7 +53,7 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
var viewModel = (AddProfileViewModel)((BindingExpression)owner).DataItem; var viewModel = (AddProfileViewModel)((BindingExpression)owner).DataItem;
if (viewModel.ProfileData != null && viewModel.ProfileData.Profiles.Any(p => p.Name.ToLower() == ((string)value).ToLower())) if (viewModel.ProfileData != null && viewModel.ProfileData.Profiles.Any(p => p.Name.ToLower().Equals(((string)value).ToLower())))
return new ValidationResult(false, "Profile name already exist"); return new ValidationResult(false, "Profile name already exist");
if (string.IsNullOrEmpty(((string)value).Trim())) if (string.IsNullOrEmpty(((string)value).Trim()))

View file

@ -1,52 +1,44 @@
using MSFSPopoutPanelManager.DomainModel.Profile; using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.Orchestration; using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.WindowsAgent; using MSFSPopoutPanelManager.WindowsAgent;
using PropertyChanged; using PropertyChanged;
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Windows.Documents;
namespace MSFSPopoutPanelManager.MainApp.ViewModel namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
[SuppressPropertyChangedWarnings] [SuppressPropertyChangedWarnings]
public class ApplicationViewModel : BaseViewModel public class ApplicationViewModel : BaseViewModel
{ {
public IntPtr ApplicationHandle private readonly AppOrchestrator _appOrchestrator;
{
get => Orchestrator.ApplicationHandle;
set => Orchestrator.ApplicationHandle = value;
}
public Window ApplicationWindow { set => Orchestrator.ApplicationWindow = value; }
public WindowState InitialWindowState { get; private set; } public WindowState InitialWindowState { get; private set; }
public ApplicationViewModel(MainOrchestrator orchestrator) : base(orchestrator) { } public ApplicationViewModel(SharedStorage sharedStorage, AppOrchestrator appOrchestrator) : base(sharedStorage)
{
_appOrchestrator = appOrchestrator;
}
public void Initialize() public void Initialize()
{ {
// Set title bar color // Set title bar color
WindowActionManager.SetWindowTitleBarColor(ApplicationHandle, "303030"); WindowActionManager.SetWindowTitleBarColor(ApplicationHandle, "303030");
Orchestrator.Initialize(); _appOrchestrator.Initialize();
Orchestrator.AppSettingData.AlwaysOnTopChanged += (sender, e) => WindowActionManager.ApplyAlwaysOnTop(ApplicationHandle, PanelType.PopOutManager, e); AppSettingData.OnAlwaysOnTopChanged += (_, e) => WindowActionManager.ApplyAlwaysOnTop(ApplicationHandle, PanelType.PopOutManager, e);
// Set window state // Set window state
if (Orchestrator.AppSettingData.ApplicationSetting.GeneralSetting.StartMinimized) if (AppSettingData.ApplicationSetting.GeneralSetting.StartMinimized)
InitialWindowState = WindowState.Minimized; InitialWindowState = WindowState.Minimized;
// Set Always on Top // Set Always on Top
if (Orchestrator.AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop) if (AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop)
WindowActionManager.ApplyAlwaysOnTop(ApplicationHandle, PanelType.PopOutManager, Orchestrator.AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop); WindowActionManager.ApplyAlwaysOnTop(ApplicationHandle, PanelType.PopOutManager, AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop);
} }
public void WindowClosing() public void WindowClosing()
{ {
Orchestrator.ApplicationClose(); _appOrchestrator.ApplicationClose();
if (Application.Current != null) if (Application.Current != null)
Environment.Exit(0); Environment.Exit(0);

View file

@ -1,65 +1,58 @@
using MSFSPopoutPanelManager.Orchestration; using System;
using System.Windows;
using MSFSPopoutPanelManager.DomainModel.Profile;
using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System;
using System.Collections.Generic;
using System.Windows.Documents;
using System.Windows.Media;
namespace MSFSPopoutPanelManager.MainApp.ViewModel namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
public abstract class BaseViewModel : ObservableObject public abstract class BaseViewModel : ObservableObject
{ {
private SharedStorage SharedStorage { get; }
protected const string ROOT_DIALOG_HOST = "RootDialog"; protected const string ROOT_DIALOG_HOST = "RootDialog";
protected MainOrchestrator Orchestrator { get; set; } protected BaseViewModel(SharedStorage sharedStorage)
public BaseViewModel(MainOrchestrator orchestrator)
{ {
Orchestrator = orchestrator; SharedStorage = sharedStorage;
InitializeChildPropertyChangeBinding();
Orchestrator.PanelPopOut.OnPopOutStarted += (sender, e) => Orchestrator.PanelPopOut.IsDisabledStartPopOut = true;
Orchestrator.PanelPopOut.OnPopOutCompleted += (sender, e) => Orchestrator.PanelPopOut.IsDisabledStartPopOut = false;
Orchestrator.PanelSource.OnPanelSourceSelectionStarted += (sender, e) => Orchestrator.PanelPopOut.IsDisabledStartPopOut = true;
Orchestrator.PanelSource.OnPanelSourceSelectionCompleted += (sender, e) => Orchestrator.PanelPopOut.IsDisabledStartPopOut = false;
} }
public AppSettingData AppSettingData => Orchestrator.AppSettingData; public AppSettingData AppSettingData => SharedStorage.AppSettingData;
public ProfileData ProfileData => Orchestrator.ProfileData; public ProfileData ProfileData
public FlightSimData FlightSimData => Orchestrator.FlightSimData;
protected List<Run> FormatStatusMessages(List<StatusMessage> messages)
{ {
List<Run> runs = new List<Run>(); get => SharedStorage.ProfileData;
set => SharedStorage.ProfileData = value;
}
foreach (var statusMessage in messages) public FlightSimData FlightSimData => SharedStorage.FlightSimData;
public UserProfile ActiveProfile => SharedStorage.ProfileData.ActiveProfile;
public IntPtr ApplicationHandle
{
get => SharedStorage.ApplicationHandle;
set => SharedStorage.ApplicationHandle = value;
}
public Window ApplicationWindow
{
get => SharedStorage.ApplicationWindow;
set => SharedStorage.ApplicationWindow = value;
}
public bool LocalCompileOnly
{
get
{ {
var run = new Run(); #if LOCAL
run.Text = statusMessage.Message; return true;
#endif
switch (statusMessage.StatusMessageType) return false;
{
case StatusMessageType.Success:
run.Foreground = new SolidColorBrush(Colors.LimeGreen);
break;
case StatusMessageType.Failure:
run.Foreground = new SolidColorBrush(Colors.IndianRed);
break;
case StatusMessageType.Executing:
run.Foreground = new SolidColorBrush(Colors.NavajoWhite);
break;
case StatusMessageType.Info:
break;
}
runs.Add(run);
if (statusMessage.NewLine)
runs.Add(new Run { Text = Environment.NewLine });
} }
return runs;
} }
} }
} }

View file

@ -8,7 +8,7 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
get get
{ {
TextInfo textInfo = new CultureInfo("en-US", false).TextInfo; var textInfo = new CultureInfo("en-US", false).TextInfo;
return $"Confirm {textInfo.ToTitleCase(ConfirmButtonText.ToLower())}"; return $"Confirm {textInfo.ToTitleCase(ConfirmButtonText.ToLower())}";
} }
} }

View file

@ -1,4 +1,5 @@
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog;
using MSFSPopoutPanelManager.Orchestration; using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.WindowsAgent; using MSFSPopoutPanelManager.WindowsAgent;
using Prism.Commands; using Prism.Commands;
@ -9,6 +10,8 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
public class HelpViewModel : BaseViewModel public class HelpViewModel : BaseViewModel
{ {
private readonly HelpOrchestrator _helpOrchestrator;
public DelegateCommand<string> HyperLinkCommand { get; private set; } public DelegateCommand<string> HyperLinkCommand { get; private set; }
public ICommand DeleteAppCacheCommand { get; private set; } public ICommand DeleteAppCacheCommand { get; private set; }
@ -21,23 +24,26 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
public bool HasOrphanAppCache { get; private set; } public bool HasOrphanAppCache { get; private set; }
public HelpViewModel(MainOrchestrator orchestrator) : base(orchestrator) public HelpViewModel(SharedStorage sharedStorage, HelpOrchestrator helpOrchestrator) : base(sharedStorage)
{ {
_helpOrchestrator = helpOrchestrator;
HyperLinkCommand = new DelegateCommand<string>(OnHyperLinkActivated); HyperLinkCommand = new DelegateCommand<string>(OnHyperLinkActivated);
DeleteAppCacheCommand = new DelegateCommand(OnDeleteAppCache); DeleteAppCacheCommand = new DelegateCommand(OnDeleteAppCache);
RollBackCommand = new DelegateCommand(OnRollBack); RollBackCommand = new DelegateCommand(OnRollBack);
var buildConfig = string.Empty;
#if DEBUG #if DEBUG
buildConfig = " (Debug)"; var buildConfig = " (Debug)";
#elif LOCAL #elif LOCAL
buildConfig = " (Local)"; var buildConfig = " (Local)";
#else
var buildConfig = string.Empty;
#endif #endif
ApplicationVersion = $"{WindowProcessManager.GetApplicationVersion()}{buildConfig}"; ApplicationVersion = $"{WindowProcessManager.GetApplicationVersion()}{buildConfig}";
IsRollBackCommandVisible = Orchestrator.Help.IsRollBackUpdateEnabled(); IsRollBackCommandVisible = _helpOrchestrator.IsRollBackUpdateEnabled();
HasOrphanAppCache = Orchestrator.Help.HasOrphanAppCache(); HasOrphanAppCache = _helpOrchestrator.HasOrphanAppCache();
} }
private void OnHyperLinkActivated(string commandParameter) private void OnHyperLinkActivated(string commandParameter)
@ -45,36 +51,36 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
switch (commandParameter) switch (commandParameter)
{ {
case "Getting Started": case "Getting Started":
Orchestrator.Help.OpenGettingStarted(); _helpOrchestrator.OpenGettingStarted();
break; break;
case "User Guide": case "User Guide":
Orchestrator.Help.OpenUserGuide(); _helpOrchestrator.OpenUserGuide();
break; break;
case "Download Latest GitHub": case "Download Latest GitHub":
Orchestrator.Help.OpenLatestDownloadGitHub(); _helpOrchestrator.OpenLatestDownloadGitHub();
break; break;
case "Download Latest FlightsimTo": case "Download Latest FlightsimTo":
Orchestrator.Help.OpenLatestDownloadFligthsimTo(); _helpOrchestrator.OpenLatestDownloadFligthsimTo();
break; break;
case "License": case "License":
Orchestrator.Help.OpenLicense(); _helpOrchestrator.OpenLicense();
break; break;
case "Version Info": case "Version Info":
Orchestrator.Help.OpenVersionInfo(); _helpOrchestrator.OpenVersionInfo();
break; break;
case "Open Data Folder": case "Open Data Folder":
Orchestrator.Help.OpenDataFolder(); _helpOrchestrator.OpenDataFolder();
break; break;
case "Download VCC Library": case "Download VCC Library":
Orchestrator.Help.DownloadVCCLibrary(); _helpOrchestrator.DownloadVccLibrary();
break; break;
} }
} }
private void OnDeleteAppCache() private void OnDeleteAppCache()
{ {
Orchestrator.Help.DeleteAppCache(); _helpOrchestrator.DeleteAppCache();
HasOrphanAppCache = Orchestrator.Help.HasOrphanAppCache(); HasOrphanAppCache = _helpOrchestrator.HasOrphanAppCache();
} }
private async void OnRollBack() private async void OnRollBack()
@ -83,7 +89,7 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
if (result != null && result.Equals("CONFIRM")) if (result != null && result.Equals("CONFIRM"))
{ {
Orchestrator.Help.RollBackUpdate(); _helpOrchestrator.RollBackUpdate();
} }
} }
} }

View file

@ -11,6 +11,8 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
public class HudBarViewModel : BaseViewModel public class HudBarViewModel : BaseViewModel
{ {
private readonly FlightSimOrchestrator _flightSimOrchestrator;
public ICommand IncreaseSimRateCommand { get; } public ICommand IncreaseSimRateCommand { get; }
public ICommand DecreaseSimRateCommand { get; } public ICommand DecreaseSimRateCommand { get; }
@ -23,18 +25,20 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
public Guid PanelId { get; set; } public Guid PanelId { get; set; }
public PanelConfig PanelConfig => ProfileData.ActiveProfile.PanelConfigs.First(p => p.Id == PanelId); public PanelConfig PanelConfig => ActiveProfile.PanelConfigs.FirstOrDefault(p => p.Id == PanelId);
public bool IsTimerStarted { get; private set; } public bool IsTimerStarted { get; private set; }
public DateTime Timer { get; set; } public DateTime Timer { get; set; }
public string HudBarTypeText => ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.HudBarType.ToString().Replace("_", " "); public string HudBarTypeText => ActiveProfile.ProfileSetting.HudBarConfig.HudBarType.ToString().Replace("_", " ");
private Timer _clock; private readonly Timer _clock;
public HudBarViewModel(MainOrchestrator orchestrator) : base(orchestrator) public HudBarViewModel(SharedStorage sharedStorage, FlightSimOrchestrator flightSimOrchestrator) : base(sharedStorage)
{ {
_flightSimOrchestrator = flightSimOrchestrator;
IncreaseSimRateCommand = new DelegateCommand(OnIncreaseSimRate); IncreaseSimRateCommand = new DelegateCommand(OnIncreaseSimRate);
DecreaseSimRateCommand = new DelegateCommand(OnDecreaseSimRate); DecreaseSimRateCommand = new DelegateCommand(OnDecreaseSimRate);
StartStopTimerCommand = new DelegateCommand(OnStartStopTimer); StartStopTimerCommand = new DelegateCommand(OnStartStopTimer);
@ -46,19 +50,19 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
_clock = new Timer(); _clock = new Timer();
_clock.Interval = 1000; _clock.Interval = 1000;
_clock.Enabled = false; _clock.Enabled = false;
_clock.Elapsed += (sender, e) => Application.Current.Dispatcher.Invoke(() => Timer = Timer.AddSeconds(1)); _clock.Elapsed += (_, _) => Application.Current.Dispatcher.Invoke(() => Timer = Timer.AddSeconds(1));
Orchestrator.FlightSim.SetHudBarConfig(); _flightSimOrchestrator.SetHudBarConfig();
} }
private void OnIncreaseSimRate() private void OnIncreaseSimRate()
{ {
Orchestrator.FlightSim.IncreaseSimRate(); _flightSimOrchestrator.IncreaseSimRate();
} }
private void OnDecreaseSimRate() private void OnDecreaseSimRate()
{ {
Orchestrator.FlightSim.DecreaseSimRate(); _flightSimOrchestrator.DecreaseSimRate();
} }
private void OnStartStopTimer() private void OnStartStopTimer()
@ -76,7 +80,7 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
private void OnClose() private void OnClose()
{ {
Orchestrator.FlightSim.StopHudBar(); _flightSimOrchestrator.StopHudBar();
} }
} }
} }

View file

@ -7,6 +7,8 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media;
using Colors = System.Windows.Media.Colors;
namespace MSFSPopoutPanelManager.MainApp.ViewModel namespace MSFSPopoutPanelManager.MainApp.ViewModel
{ {
@ -26,7 +28,7 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
public bool IsVisible public bool IsVisible
{ {
get { return _isVisible; } get => _isVisible;
set set
{ {
if (!AppSettingData.ApplicationSetting.PopOutSetting.EnablePopOutMessages) if (!AppSettingData.ApplicationSetting.PopOutSetting.EnablePopOutMessages)
@ -44,36 +46,36 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
public int WindowHeight { get; set; } public int WindowHeight { get; set; }
public MessageWindowViewModel(MainOrchestrator orchestrator) : base(orchestrator) public MessageWindowViewModel(SharedStorage sharedStorage, PanelSourceOrchestrator panelSourceOrchestrator, PanelPopOutOrchestrator panelPopOutOrchestrator) : base(sharedStorage)
{ {
IsVisible = false; IsVisible = false;
Orchestrator.PanelPopOut.OnPopOutStarted += (sender, e) => panelPopOutOrchestrator.OnPopOutStarted += (_, _) =>
{ {
IsVisible = true; IsVisible = true;
WindowWidth = WINDOW_WIDTH_POPOUT_MESSAGE; WindowWidth = WINDOW_WIDTH_POPOUT_MESSAGE;
WindowHeight = WINDOW_HEIGHT_POPOUT_MESSAGE; WindowHeight = WINDOW_HEIGHT_POPOUT_MESSAGE;
}; };
Orchestrator.PanelPopOut.OnPopOutCompleted += (sender, e) => panelPopOutOrchestrator.OnPopOutCompleted += (_, _) =>
{ {
Thread.Sleep(1000); Thread.Sleep(1000);
IsVisible = false; IsVisible = false;
WindowWidth = WINDOW_WIDTH_POPOUT_MESSAGE; WindowWidth = WINDOW_WIDTH_POPOUT_MESSAGE;
WindowHeight = WINDOW_HEIGHT_POPOUT_MESSAGE; WindowHeight = WINDOW_HEIGHT_POPOUT_MESSAGE;
}; };
Orchestrator.PanelSource.OnStatusMessageStarted += (sender, e) => panelSourceOrchestrator.OnStatusMessageStarted += (_, _) =>
{ {
IsVisible = true; IsVisible = true;
WindowWidth = WINDOW_WIDTH_REGULAR_MESSAGE; WindowWidth = WINDOW_WIDTH_REGULAR_MESSAGE;
WindowHeight = WINDOW_HEIGHT_REGULAR_MESSAGE; WindowHeight = WINDOW_HEIGHT_REGULAR_MESSAGE;
}; };
Orchestrator.PanelSource.OnStatusMessageEnded += (sender, e) => panelSourceOrchestrator.OnStatusMessageEnded += (_, _) =>
{ {
IsVisible = false; IsVisible = false;
WindowWidth = WINDOW_WIDTH_REGULAR_MESSAGE; WindowWidth = WINDOW_WIDTH_REGULAR_MESSAGE;
WindowHeight = WINDOW_HEIGHT_REGULAR_MESSAGE; WindowHeight = WINDOW_HEIGHT_REGULAR_MESSAGE;
}; };
StatusMessageWriter.OnStatusMessage += (sender, e) => StatusMessageWriter.OnStatusMessage += (_, e) =>
{ {
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
@ -99,5 +101,43 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
WindowActionManager.MoveWindow(Handle, left, top, WindowWidth, WindowHeight); WindowActionManager.MoveWindow(Handle, left, top, WindowWidth, WindowHeight);
WindowActionManager.ApplyAlwaysOnTop(Handle, PanelType.StatusMessageWindow, true); WindowActionManager.ApplyAlwaysOnTop(Handle, PanelType.StatusMessageWindow, true);
} }
private List<Run> FormatStatusMessages(List<StatusMessage> messages)
{
var runs = new List<Run>
{
Capacity = 0
};
foreach (var statusMessage in messages)
{
var run = new Run
{
Text = statusMessage.Message
};
switch (statusMessage.StatusMessageType)
{
case StatusMessageType.Success:
run.Foreground = new SolidColorBrush(Colors.LimeGreen);
break;
case StatusMessageType.Failure:
run.Foreground = new SolidColorBrush(Colors.IndianRed);
break;
case StatusMessageType.Executing:
run.Foreground = new SolidColorBrush(Colors.NavajoWhite);
break;
case StatusMessageType.Info:
break;
}
runs.Add(run);
if (statusMessage.NewLine)
runs.Add(new Run { Text = Environment.NewLine });
}
return runs;
}
} }
} }

Some files were not shown because too many files have changed in this diff Show more