summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrakeWPF
diff options
context:
space:
mode:
authorsr55 <[email protected]>2016-05-12 19:48:41 +0100
committersr55 <[email protected]>2016-05-12 19:48:41 +0100
commit16314ab0db954c46f64632e4111836061bd9748d (patch)
tree31e4e782eef340b81be7c5ad435ca79295f49aa3 /win/CS/HandBrakeWPF
parentc2ddfa317feb249bc4c39e986e9e60f9208bde0a (diff)
WinGui: Add a track list on the Audio Defaults screen. This changes the behaviour to match the Mac and Linux GUIs. So you no longer have to have a source audio track before you can create presets.
Diffstat (limited to 'win/CS/HandBrakeWPF')
-rw-r--r--win/CS/HandBrakeWPF/HandBrakeWPF.csproj1
-rw-r--r--win/CS/HandBrakeWPF/Model/Audio/AudioBehaviourTrack.cs598
-rw-r--r--win/CS/HandBrakeWPF/Model/Audio/AudioBehaviours.cs18
-rw-r--r--win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs20
-rw-r--r--win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs6
-rw-r--r--win/CS/HandBrakeWPF/Services/Presets/PresetService.cs2
-rw-r--r--win/CS/HandBrakeWPF/ViewModels/AudioDefaultsViewModel.cs164
-rw-r--r--win/CS/HandBrakeWPF/ViewModels/AudioViewModel.cs19
-rw-r--r--win/CS/HandBrakeWPF/Views/AudioDefaultsView.xaml161
-rw-r--r--win/CS/HandBrakeWPF/Views/PopupWindowView.xaml6
10 files changed, 924 insertions, 71 deletions
diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
index cfb5e8596..08ae3899a 100644
--- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
+++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj
@@ -168,6 +168,7 @@
<Compile Include="Helpers\MP4Helper.cs" />
<Compile Include="Helpers\TimeSpanHelper.cs" />
<Compile Include="Helpers\Validate.cs" />
+ <Compile Include="Model\Audio\AudioBehaviourTrack.cs" />
<Compile Include="Model\Audio\AudioTrackDefaultsMode.cs" />
<Compile Include="Model\Audio\AudioBehaviourModes.cs" />
<Compile Include="Model\Audio\AudioBehaviours.cs" />
diff --git a/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviourTrack.cs b/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviourTrack.cs
new file mode 100644
index 000000000..b775c06b7
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviourTrack.cs
@@ -0,0 +1,598 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="AudioTrack.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Model of a HandBrake Audio Track and it's associated behaviours.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Model.Audio
+{
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+
+ using HandBrake.ApplicationServices.Interop;
+ using HandBrake.ApplicationServices.Interop.Model;
+ using HandBrake.ApplicationServices.Interop.Model.Encoding;
+
+ using Services.Encode.Model.Models;
+ using Utilities;
+
+ using Newtonsoft.Json;
+
+ /// <summary>
+ /// Model of a HandBrake Audio Track and it's associated behaviours.
+ /// </summary>
+ public class AudioBehaviourTrack : PropertyChangedBase
+ {
+ private int bitrate;
+ private double drc;
+ private AudioEncoder encoder;
+ private int gain;
+ private HBMixdown mixDown;
+ private double sampleRate;
+ private bool isDefault;
+ private IEnumerable<int> bitrates;
+ private IEnumerable<double> encoderQualityValues;
+ private AudioEncoderRateType encoderRateType;
+ private double? quality;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref = "AudioTrack" /> class.
+ /// </summary>
+ public AudioBehaviourTrack()
+ {
+ // Default Values
+ this.Encoder = AudioEncoder.ffaac;
+ this.MixDown = HandBrakeEncoderHelpers.Mixdowns.FirstOrDefault(m => m.ShortName == "dpl2");
+ this.SampleRate = 48;
+ this.Bitrate = 160;
+ this.DRC = 0;
+ this.EncoderRateType = AudioEncoderRateType.Bitrate;
+ this.SetupLimits();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioTrack"/> class.
+ /// Copy Constructor
+ /// </summary>
+ /// <param name="track">
+ /// The track.
+ /// </param>
+ public AudioBehaviourTrack(AudioBehaviourTrack track)
+ {
+ this.bitrate = track.Bitrate;
+ this.drc = track.DRC;
+ this.encoder = track.Encoder;
+ this.gain = track.Gain;
+ this.mixDown = track.MixDown;
+ this.sampleRate = track.SampleRate;
+ this.Quality = track.Quality;
+ this.encoderRateType = track.EncoderRateType;
+ this.SetupLimits();
+ }
+
+ #region Track Properties
+
+ /// <summary>
+ /// Gets or sets Dynamic Range Compression
+ /// </summary>
+ public double DRC
+ {
+ get
+ {
+ return this.drc;
+ }
+
+ set
+ {
+ if (!Equals(value, this.drc))
+ {
+ this.drc = value;
+ this.NotifyOfPropertyChange(() => this.DRC);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the Gain for the audio track
+ /// </summary>
+ public int Gain
+ {
+ get
+ {
+ return this.gain;
+ }
+
+ set
+ {
+ if (!Equals(value, this.gain))
+ {
+ this.gain = value;
+ this.NotifyOfPropertyChange(() => this.Gain);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets Audio Mixdown (ShortName)
+ /// </summary>
+ public HBMixdown MixDown
+ {
+ get
+ {
+ return this.IsPassthru ? null : this.mixDown;
+ }
+
+ set
+ {
+ if (!object.Equals(this.mixDown, value))
+ {
+ this.mixDown = value;
+ this.NotifyOfPropertyChange(() => this.MixDown);
+ this.SetupLimits();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets Audio Encoder
+ /// </summary>
+ public AudioEncoder Encoder
+ {
+ get
+ {
+ return this.encoder;
+ }
+
+ set
+ {
+ if (object.Equals(this.encoder, value))
+ {
+ return;
+ }
+
+ this.encoder = value;
+ this.NotifyOfPropertyChange(() => this.Encoder);
+ this.NotifyOfPropertyChange(() => this.IsPassthru);
+ this.NotifyOfPropertyChange(() => this.IsBitrateVisible);
+ this.NotifyOfPropertyChange(() => this.IsQualityVisible);
+ this.NotifyOfPropertyChange(() => this.IsRateTypeVisible);
+ this.NotifyOfPropertyChange(() => this.TrackReference);
+ this.GetDefaultMixdownIfNull();
+ this.SetupLimits();
+
+ // Refresh the available encoder rate types.
+ this.NotifyOfPropertyChange(() => this.AudioEncoderRateTypes);
+ if (!this.AudioEncoderRateTypes.Contains(this.EncoderRateType))
+ {
+ this.EncoderRateType = AudioEncoderRateType.Bitrate; // Default to bitrate.
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets Audio SampleRate
+ /// </summary>
+ public double SampleRate
+ {
+ get
+ {
+ return this.sampleRate;
+ }
+
+ set
+ {
+ this.sampleRate = value;
+ this.NotifyOfPropertyChange(() => this.SampleRate);
+ this.SetupLimits();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the encoder rate type.
+ /// </summary>
+ public AudioEncoderRateType EncoderRateType
+ {
+ get
+ {
+ return this.encoderRateType;
+ }
+
+ set
+ {
+ this.encoderRateType = value;
+ this.SetupLimits();
+ this.NotifyOfPropertyChange(() => this.EncoderRateType);
+ this.NotifyOfPropertyChange(() => this.IsBitrateVisible);
+ this.NotifyOfPropertyChange(() => this.IsQualityVisible);
+
+ if (!this.Quality.HasValue)
+ {
+ HBAudioEncoder hbAudioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(this.Encoder));
+ this.Quality = HandBrakeEncoderHelpers.GetDefaultQuality(hbAudioEncoder);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets Audio Bitrate
+ /// </summary>
+ public int Bitrate
+ {
+ get
+ {
+ return this.bitrate;
+ }
+
+ set
+ {
+ this.bitrate = value;
+ this.NotifyOfPropertyChange(() => this.Bitrate);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets Audio quality
+ /// </summary>
+ public double? Quality
+ {
+ get
+ {
+ return this.quality;
+ }
+
+ set
+ {
+ this.quality = value;
+ this.NotifyOfPropertyChange(() => this.quality);
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets AudioEncoderDisplayValue.
+ /// </summary>
+ [JsonIgnore]
+ public string AudioEncoderDisplayValue
+ {
+ get
+ {
+ return EnumHelper<AudioEncoder>.GetDisplay(this.Encoder);
+ }
+ }
+
+ /// <summary>
+ /// Gets the The UI display value for bit rate
+ /// </summary>
+ [JsonIgnore]
+ public string BitRateDisplayValue
+ {
+ get
+ {
+ if (this.Encoder == AudioEncoder.Ac3Passthrough || this.Encoder == AudioEncoder.DtsPassthrough
+ || this.Encoder == AudioEncoder.DtsHDPassthrough)
+ {
+ return "Auto";
+ }
+
+ return this.Bitrate.ToString();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether is default.
+ /// TODO - Can this be removed? May have been added as a quick fix for a styling quirk.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsDefault
+ {
+ get
+ {
+ return this.isDefault;
+ }
+ set
+ {
+ this.isDefault = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the The UI display value for sample rate
+ /// </summary>
+ [JsonIgnore]
+ public string SampleRateDisplayValue
+ {
+ get
+ {
+ return this.SampleRate == 0 ? "Auto" : this.SampleRate.ToString(CultureInfo.InvariantCulture);
+ }
+ set
+ {
+ // TODO change this to be a converted field
+ if (string.IsNullOrEmpty(value))
+ {
+ return;
+ }
+
+ double samplerate;
+ double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out samplerate);
+
+ this.SampleRate = samplerate;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether IsPassthru.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsPassthru
+ {
+ get
+ {
+ if (this.Encoder == AudioEncoder.Ac3Passthrough || this.Encoder == AudioEncoder.DtsPassthrough
+ || this.Encoder == AudioEncoder.DtsHDPassthrough || this.Encoder == AudioEncoder.AacPassthru
+ || this.Encoder == AudioEncoder.Mp3Passthru || this.Encoder == AudioEncoder.Passthrough ||
+ this.Encoder == AudioEncoder.EAc3Passthrough || this.Encoder == AudioEncoder.TrueHDPassthrough
+ || this.Encoder == AudioEncoder.FlacPassthru)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets the bitrates.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<int> Bitrates
+ {
+ get
+ {
+ return this.bitrates;
+ }
+ }
+
+ /// <summary>
+ /// Gets the quality compression values.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<double> EncoderQualityValues
+ {
+ get
+ {
+ return this.encoderQualityValues;
+ }
+ }
+
+ /// <summary>
+ /// Gets the audio encoder rate types.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<AudioEncoderRateType> AudioEncoderRateTypes
+ {
+ get
+ {
+ IList<AudioEncoderRateType> types = EnumHelper<AudioEncoderRateType>.GetEnumList().ToList();
+ HBAudioEncoder hbaenc = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(this.Encoder));
+ if (hbaenc == null || !hbaenc.SupportsQuality)
+ {
+ types.Remove(AudioEncoderRateType.Quality);
+ }
+
+ return types;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether can set bitrate.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsBitrateVisible
+ {
+ get
+ {
+ if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24)
+ {
+ return false;
+ }
+
+ return Equals(this.EncoderRateType, AudioEncoderRateType.Bitrate);
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether is quality visible.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsQualityVisible
+ {
+ get
+ {
+ if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24)
+ {
+ return false;
+ }
+
+ return Equals(this.EncoderRateType, AudioEncoderRateType.Quality);
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether is rate type visible.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsRateTypeVisible
+ {
+ get
+ {
+ if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether IsLossless.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsLossless
+ {
+ get
+ {
+ return this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24;
+ }
+ }
+
+ /// <summary>
+ /// Gets TrackReference.
+ /// </summary>
+ [JsonIgnore]
+ public AudioBehaviourTrack TrackReference
+ {
+ get { return this; }
+ }
+
+ #region Handler Methods
+
+ /// <summary>
+ /// The setup limits.
+ /// </summary>
+ private void SetupLimits()
+ {
+ this.SetupBitrateLimits();
+ this.SetupQualityCompressionLimits();
+ }
+
+ /// <summary>
+ /// The calculate bitrate limits.
+ /// </summary>
+ private void SetupBitrateLimits()
+ {
+ // Base set of bitrates available.
+ List<int> audioBitrates = HandBrakeEncoderHelpers.AudioBitrates;
+
+ // Defaults
+ int max = 256;
+ int low = 32;
+
+ // Based on the users settings, find the high and low bitrates.
+ HBAudioEncoder hbaenc = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(this.Encoder));
+ HBRate rate = HandBrakeEncoderHelpers.AudioSampleRates.FirstOrDefault(t => t.Name == this.SampleRate.ToString(CultureInfo.InvariantCulture));
+ HBMixdown mixdown = this.mixDown ?? HandBrakeEncoderHelpers.GetMixdown("dpl2");
+
+ BitrateLimits limits = HandBrakeEncoderHelpers.GetBitrateLimits(hbaenc, rate != null ? rate.Rate : 48000, mixdown);
+ if (limits != null)
+ {
+ max = limits.High;
+ low = limits.Low;
+ }
+
+ // Return the subset of available bitrates.
+ List<int> subsetBitrates = audioBitrates.Where(b => b <= max && b >= low).ToList();
+ this.bitrates = subsetBitrates;
+ this.NotifyOfPropertyChange(() => this.Bitrates);
+
+ // If the subset does not contain the current bitrate, request the default.
+ if (!subsetBitrates.Contains(this.Bitrate))
+ {
+ this.Bitrate = HandBrakeEncoderHelpers.GetDefaultBitrate(hbaenc, rate != null ? rate.Rate : 48000, mixdown);
+ }
+ }
+
+ /// <summary>
+ /// The setup quality compression limits.
+ /// </summary>
+ private void SetupQualityCompressionLimits()
+ {
+ HBAudioEncoder hbAudioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(this.Encoder));
+ if (hbAudioEncoder.SupportsQuality)
+ {
+ RangeLimits limits = null;
+
+ if (hbAudioEncoder.SupportsQuality)
+ {
+ limits = hbAudioEncoder.QualityLimits;
+ }
+
+ if (limits != null)
+ {
+ double value = limits.Ascending ? limits.Low : limits.High;
+ List<double> values = new List<double> { value };
+
+ if (limits.Ascending)
+ {
+ while (value < limits.High)
+ {
+ value += limits.Granularity;
+ values.Add(value);
+ }
+ }
+ else
+ {
+ while (value > limits.Low)
+ {
+ value -= limits.Granularity;
+ values.Add(value);
+ }
+ }
+
+ this.encoderQualityValues = values;
+ }
+ else
+ {
+ this.encoderQualityValues = new List<double>();
+ }
+ }
+ else
+ {
+ this.encoderQualityValues = new List<double>();
+ }
+
+ // Default the audio quality value if it's out of range.
+ if (Equals(this.EncoderRateType, AudioEncoderRateType.Quality))
+ {
+ if (this.Quality.HasValue && !this.encoderQualityValues.Contains(this.Quality.Value))
+ {
+ this.Quality = HandBrakeEncoderHelpers.GetDefaultQuality(hbAudioEncoder);
+ }
+ }
+
+ this.NotifyOfPropertyChange(() => this.EncoderQualityValues);
+ }
+
+ /// <summary>
+ /// Set the default mixdown when the mixdown is null or "none"
+ /// </summary>
+ private void GetDefaultMixdownIfNull()
+ {
+ //if (this.ScannedTrack == null)
+ //{
+ // return;
+ //}
+
+ //HBAudioEncoder aencoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(this.encoder));
+ //HBMixdown currentMixdown = HandBrakeEncoderHelpers.GetMixdown(this.mixDown);
+ //HBMixdown sanitisedMixdown = HandBrakeEncoderHelpers.SanitizeMixdown(currentMixdown, aencoder, (uint)this.ScannedTrack.ChannelLayout);
+ //HBMixdown defaultMixdown = HandBrakeEncoderHelpers.GetDefaultMixdown(aencoder, (uint)this.ScannedTrack.ChannelLayout);
+
+ //if (this.mixDown == null || this.mixDown == "none")
+ //{
+ // this.MixDown = defaultMixdown.ShortName;
+ //}
+ //else if (sanitisedMixdown != null)
+ //{
+ // this.MixDown = sanitisedMixdown.ShortName;
+ //}
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviours.cs b/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviours.cs
index e88f1ade6..b0f81d0c4 100644
--- a/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviours.cs
+++ b/win/CS/HandBrakeWPF/Model/Audio/AudioBehaviours.cs
@@ -31,6 +31,7 @@ namespace HandBrakeWPF.Model.Audio
this.SelectedBehaviour = AudioBehaviourModes.None;
this.SelectedTrackDefaultBehaviour = AudioTrackDefaultsMode.None;
this.SelectedLangauges = new BindingList<string>();
+ this.BehaviourTracks = new BindingList<AudioBehaviourTrack>();
}
/// <summary>
@@ -44,6 +45,7 @@ namespace HandBrakeWPF.Model.Audio
this.SelectedBehaviour = behaviours.SelectedBehaviour;
this.SelectedTrackDefaultBehaviour = behaviours.SelectedTrackDefaultBehaviour;
this.SelectedLangauges = new BindingList<string>(behaviours.selectedLangauges.ToList());
+ this.BehaviourTracks = behaviours.BehaviourTracks;
}
/// <summary>
@@ -108,6 +110,11 @@ namespace HandBrakeWPF.Model.Audio
}
/// <summary>
+ /// The list of track templates we are going to use to generate audio tracks for a source.
+ /// </summary>
+ public BindingList<AudioBehaviourTrack> BehaviourTracks { get; set; }
+
+ /// <summary>
/// Clone this object
/// </summary>
/// <returns>
@@ -116,11 +123,12 @@ namespace HandBrakeWPF.Model.Audio
public AudioBehaviours Clone()
{
AudioBehaviours cloned = new AudioBehaviours
- {
- SelectedBehaviour = this.selectedBehaviour,
- SelectedLangauges = new BindingList<string>(),
- SelectedTrackDefaultBehaviour = this.SelectedTrackDefaultBehaviour
- };
+ {
+ SelectedBehaviour = this.selectedBehaviour,
+ SelectedLangauges = new BindingList<string>(),
+ SelectedTrackDefaultBehaviour = this.SelectedTrackDefaultBehaviour,
+ BehaviourTracks = this.BehaviourTracks
+ };
foreach (var item in this.SelectedLangauges)
{
diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs
index 53b162f87..8e21820c2 100644
--- a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs
+++ b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs
@@ -20,6 +20,7 @@ namespace HandBrakeWPF.Services.Encode.Model.Models
using HandBrake.ApplicationServices.Interop.Model.Encoding;
using HandBrake.ApplicationServices.Utilities;
+ using HandBrakeWPF.Model.Audio;
using HandBrakeWPF.Services.Scan.Model;
using HandBrakeWPF.Utilities;
@@ -94,6 +95,25 @@ namespace HandBrakeWPF.Services.Encode.Model.Models
this.SetupLimits();
}
+ /// <summary>
+ /// Create a track from a behaviour track.
+ /// </summary>
+ /// <param name="track">The Behavior track</param>
+ public AudioTrack(AudioBehaviourTrack track)
+ {
+ this.drc = track.DRC;
+ this.encoder = track.Encoder;
+ this.gain = track.Gain;
+ this.mixDown = track.MixDown != null ? track.MixDown.ShortName : "dpl2";
+ this.sampleRate = track.SampleRate;
+ this.scannedTrack = new Audio();
+ this.encoderRateType = track.EncoderRateType;
+ this.quality = track.Quality;
+ this.bitrate = track.Bitrate;
+
+ this.SetupLimits();
+ }
+
#region Track Properties
/// <summary>
diff --git a/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs b/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
index c8331076b..051d78c5f 100644
--- a/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
+++ b/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
@@ -394,13 +394,13 @@ namespace HandBrakeWPF.Services.Presets.Factories
{
foreach (var audioTrack in importedPreset.AudioList)
{
- AudioTrack track = new AudioTrack();
+ AudioBehaviourTrack track = new AudioBehaviourTrack();
track.Bitrate = audioTrack.AudioBitrate;
// track.CompressionLevel = audioTrack.AudioCompressionLevel;
// track.AudioDitherMethod = audioTrack.AudioDitherMethod;
track.Encoder = EnumHelper<AudioEncoder>.GetValue(audioTrack.AudioEncoder);
- track.MixDown = audioTrack.AudioMixdown;
+ track.MixDown = HandBrakeEncoderHelpers.GetMixdown(audioTrack.AudioMixdown);
// track.AudioNormalizeMixLevel = audioTrack.AudioNormalizeMixLevel;
track.SampleRate = audioTrack.AudioSamplerate == "auto" ? 0 : double.Parse(audioTrack.AudioSamplerate);
@@ -410,7 +410,7 @@ namespace HandBrakeWPF.Services.Presets.Factories
track.Gain = (int)audioTrack.AudioTrackGainSlider;
track.DRC = audioTrack.AudioTrackDRCSlider;
- preset.Task.AudioTracks.Add(track);
+ preset.AudioTrackBehaviours.BehaviourTracks.Add(track);
}
}
diff --git a/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs b/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs
index 79d35e2ab..55915329b 100644
--- a/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs
+++ b/win/CS/HandBrakeWPF/Services/Presets/PresetService.cs
@@ -43,7 +43,7 @@ namespace HandBrakeWPF.Services.Presets
{
#region Private Variables
- public const int ForcePresetReset = 2;
+ public const int ForcePresetReset = 3;
public static string UserPresetCatgoryName = "User Presets";
private readonly string presetFile = Path.Combine(DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()), "presets.json");
private readonly ObservableCollection<Preset> presets = new ObservableCollection<Preset>();
diff --git a/win/CS/HandBrakeWPF/ViewModels/AudioDefaultsViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/AudioDefaultsViewModel.cs
index a79902099..70de20c67 100644
--- a/win/CS/HandBrakeWPF/ViewModels/AudioDefaultsViewModel.cs
+++ b/win/CS/HandBrakeWPF/ViewModels/AudioDefaultsViewModel.cs
@@ -10,9 +10,12 @@
namespace HandBrakeWPF.ViewModels
{
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
+ using HandBrake.ApplicationServices.Interop;
+ using HandBrake.ApplicationServices.Interop.Model.Encoding;
using HandBrake.ApplicationServices.Utilities;
using HandBrakeWPF.Model.Audio;
@@ -30,7 +33,6 @@ namespace HandBrakeWPF.ViewModels
private BindingList<string> availableLanguages;
private AudioBehaviours audioBehaviours;
private EncodeTask task;
-
#region Constructors and Destructors
/// <summary>
@@ -47,6 +49,13 @@ namespace HandBrakeWPF.ViewModels
this.SelectedLangaugesToMove = new BindingList<string>();
this.AvailableLanguages = new BindingList<string>();
this.AudioEncoders = EnumHelper<AudioEncoder>.GetEnumList();
+ this.Mixdowns = new BindingList<HBMixdown>(HandBrakeEncoderHelpers.Mixdowns);
+
+ this.SampleRates = new ObservableCollection<string> { "Auto" };
+ foreach (var item in HandBrakeEncoderHelpers.AudioSampleRates)
+ {
+ this.SampleRates.Add(item.Name);
+ }
this.Setup((Preset)null, task);
}
@@ -76,9 +85,20 @@ namespace HandBrakeWPF.ViewModels
}
/// <summary>
- /// Gets or sets AudioEncoders.
+ /// Gets or sets the list of audio tracks we will use as templates for generating tracks for a given source.
/// </summary>
- public IEnumerable<AudioEncoder> AudioEncoders { get; set; }
+ public BindingList<AudioBehaviourTrack> BehaviourTracks
+ {
+ get
+ {
+ return this.AudioBehaviours.BehaviourTracks;
+ }
+ set
+ {
+ this.AudioBehaviours.BehaviourTracks = value;
+ this.NotifyOfPropertyChange(() => this.BehaviourTracks);
+ }
+ }
/// <summary>
/// Gets the audio behaviours.
@@ -102,45 +122,6 @@ namespace HandBrakeWPF.ViewModels
}
/// <summary>
- /// Gets the audio behaviour modes.
- /// </summary>
- public BindingList<AudioBehaviourModes> AudioBehaviourModeList
- {
- get
- {
- return new BindingList<AudioBehaviourModes>(EnumHelper<AudioBehaviourModes>.GetEnumList().ToList());
- }
- }
-
- /// <summary>
- /// Gets the audio track default behaviour mode list.
- /// </summary>
- public BindingList<AudioTrackDefaultsMode> AudioTrackDefaultBehaviourModeList
- {
- get
- {
- return new BindingList<AudioTrackDefaultsMode>(EnumHelper<AudioTrackDefaultsMode>.GetEnumList().ToList());
- }
- }
-
- /// <summary>
- /// Gets AvailableLanguages.
- /// </summary>
- public BindingList<string> AvailableLanguages
- {
- get
- {
- return this.availableLanguages;
- }
-
- private set
- {
- this.availableLanguages = value;
- this.NotifyOfPropertyChange("AvailableLanguages");
- }
- }
-
- /// <summary>
/// Gets SelectedLangauges.
/// </summary>
public BindingList<string> SelectedAvailableToMove { get; private set; }
@@ -308,9 +289,100 @@ namespace HandBrakeWPF.ViewModels
#endregion
+ #region Data Properties
+
+ /// <summary>
+ /// Gets the audio behaviour modes.
+ /// </summary>
+ public BindingList<AudioBehaviourModes> AudioBehaviourModeList
+ {
+ get
+ {
+ return new BindingList<AudioBehaviourModes>(EnumHelper<AudioBehaviourModes>.GetEnumList().ToList());
+ }
+ }
+
+ /// <summary>
+ /// Gets the audio track default behaviour mode list.
+ /// </summary>
+ public BindingList<AudioTrackDefaultsMode> AudioTrackDefaultBehaviourModeList
+ {
+ get
+ {
+ return new BindingList<AudioTrackDefaultsMode>(EnumHelper<AudioTrackDefaultsMode>.GetEnumList().ToList());
+ }
+ }
+
+ /// <summary>
+ /// Gets AvailableLanguages.
+ /// </summary>
+ public BindingList<string> AvailableLanguages
+ {
+ get
+ {
+ return this.availableLanguages;
+ }
+
+ private set
+ {
+ this.availableLanguages = value;
+ this.NotifyOfPropertyChange("AvailableLanguages");
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets AudioEncoders.
+ /// </summary>
+ public IEnumerable<AudioEncoder> AudioEncoders { get; set; }
+
+ /// <summary>
+ /// Gets or sets AudioEncoders.
+ /// </summary>
+ public IEnumerable<HBMixdown> Mixdowns { get; set; }
+
+
+ /// <summary>
+ /// Gets or sets AudioBitrates.
+ /// </summary>
+ public IEnumerable<int> AudioBitrates { get; set; }
+
+ /// <summary>
+ /// Gets or sets SampleRates.
+ /// </summary>
+ public IList<string> SampleRates { get; set; }
+
+ #endregion
+
#region Public Methods
/// <summary>
+ /// Add a new behaviour track.
+ /// </summary>
+ public void AddTrack()
+ {
+ this.BehaviourTracks.AddNew();
+ }
+
+ /// <summary>
+ /// Clear all the behaviour tracks
+ /// </summary>
+ public void ClearTracks()
+ {
+ this.BehaviourTracks.Clear();
+ }
+
+ /// <summary>
+ /// Remove the Selected Track
+ /// </summary>
+ /// <param name="track">
+ /// The track.
+ /// </param>
+ public void RemoveTrack(AudioBehaviourTrack track)
+ {
+ this.BehaviourTracks.Remove(track);
+ }
+
+ /// <summary>
/// Audio List Move Left
/// </summary>
public void LanguageMoveRight()
@@ -376,8 +448,7 @@ namespace HandBrakeWPF.ViewModels
public void Setup(Preset preset, EncodeTask task)
{
// Reset
- this.AudioBehaviours.SelectedBehaviour = AudioBehaviourModes.None;
- this.AudioBehaviours.SelectedLangauges.Clear();
+ this.AudioBehaviours = new AudioBehaviours();
// Setup for this Encode Task.
this.Task = task;
@@ -403,6 +474,11 @@ namespace HandBrakeWPF.ViewModels
this.AudioBehaviours.SelectedBehaviour = behaviours.SelectedBehaviour;
this.AudioBehaviours.SelectedTrackDefaultBehaviour = behaviours.SelectedTrackDefaultBehaviour;
+ foreach (AudioBehaviourTrack item in preset.AudioTrackBehaviours.BehaviourTracks)
+ {
+ this.AudioBehaviours.BehaviourTracks.Add(new AudioBehaviourTrack(item));
+ }
+
foreach (string selectedItem in behaviours.SelectedLangauges)
{
this.AvailableLanguages.Remove(selectedItem);
diff --git a/win/CS/HandBrakeWPF/ViewModels/AudioViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/AudioViewModel.cs
index 86f8b36d2..2706e8e83 100644
--- a/win/CS/HandBrakeWPF/ViewModels/AudioViewModel.cs
+++ b/win/CS/HandBrakeWPF/ViewModels/AudioViewModel.cs
@@ -79,11 +79,6 @@ namespace HandBrakeWPF.ViewModels
public IAudioDefaultsViewModel AudioDefaultsViewModel { get; set; }
/// <summary>
- /// Gets or sets AudioBitrates.
- /// </summary>
- public IEnumerable<int> AudioBitrates { get; set; }
-
- /// <summary>
/// Gets or sets AudioEncoders.
/// </summary>
public IEnumerable<AudioEncoder> AudioEncoders { get; set; }
@@ -343,13 +338,13 @@ namespace HandBrakeWPF.ViewModels
this.Task.AudioTracks.Add(new AudioTrack { ScannedTrack = track });
break;
case AudioTrackDefaultsMode.FirstTrack:
- AudioTrack template = this.currentPreset.Task.AudioTracks.FirstOrDefault();
- this.Task.AudioTracks.Add(template != null ? new AudioTrack(template, false) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
+ AudioBehaviourTrack template = this.currentPreset.AudioTrackBehaviours.BehaviourTracks.FirstOrDefault();
+ this.Task.AudioTracks.Add(template != null ? new AudioTrack(template) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
break;
case AudioTrackDefaultsMode.AllTracks:
- foreach (AudioTrack tmpl in this.currentPreset.Task.AudioTracks)
+ foreach (AudioBehaviourTrack tmpl in this.currentPreset.AudioTrackBehaviours.BehaviourTracks)
{
- this.Task.AudioTracks.Add(tmpl != null ? new AudioTrack(tmpl, false) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
+ this.Task.AudioTracks.Add(tmpl != null ? new AudioTrack(tmpl) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
}
break;
@@ -399,9 +394,9 @@ namespace HandBrakeWPF.ViewModels
}
// Step 3, Setup the tracks from the preset
- foreach (AudioTrack track in this.currentPreset.Task.AudioTracks)
+ foreach (AudioBehaviourTrack track in this.currentPreset.AudioTrackBehaviours.BehaviourTracks)
{
- this.Task.AudioTracks.Add(new AudioTrack(track, false) { ScannedTrack = this.GetPreferredAudioTrack() });
+ this.Task.AudioTracks.Add(new AudioTrack(track) { ScannedTrack = this.GetPreferredAudioTrack() });
}
// Step 4, Handle the default selection behaviour.
@@ -517,8 +512,6 @@ namespace HandBrakeWPF.ViewModels
return trackList;
}
-
-
#endregion
}
} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Views/AudioDefaultsView.xaml b/win/CS/HandBrakeWPF/Views/AudioDefaultsView.xaml
index b5d2f70a6..92844326e 100644
--- a/win/CS/HandBrakeWPF/Views/AudioDefaultsView.xaml
+++ b/win/CS/HandBrakeWPF/Views/AudioDefaultsView.xaml
@@ -9,8 +9,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="clr-namespace:HandBrakeWPF.Helpers"
xmlns:Properties="clr-namespace:HandBrakeWPF.Properties"
- d:DesignHeight="360"
- d:DesignWidth="770"
+ xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
+ xmlns:controls="clr-namespace:HandBrakeWPF.Controls"
+ d:DesignHeight="450"
+ d:DesignWidth="800"
mc:Ignorable="d"
x:Name="audioDefaultsTab">
@@ -19,8 +21,12 @@
<Audio:AudioEncoderConverter x:Key="audioEncoderConverter" />
<Audio:AudioBehaviourConverter x:Key="audioBehaviourConverter" />
<Audio:AudioTrackDefaultBehaviourConverter x:Key="audioTrackDefaultBehaviourConverter" />
+ <Conveters:BooleanToHiddenVisibilityConverter x:Key="boolToHiddenVisConverter" />
+ <Audio:AudioRateTypeConverter x:Key="audioRateTypeConverter" />
+ <Audio:AudioMixdownConverter x:Key="audioMixdownConverter" />
</UserControl.Resources>
+
<Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -155,6 +161,157 @@
</StackPanel>
+
+ <!-- Row 2 -->
+ <TextBlock Text="Automatically add Tracks" FontWeight="Bold" Margin="0,10,0,5" />
+ <StackPanel Orientation="Horizontal">
+ <Button Content="Add Track" cal:Message.Attach="[Event Click] = [Action AddTrack]" Margin="10,0,10,0" Padding="8,2"/>
+ <Button Content="Clear" cal:Message.Attach="[Event Click] = [Action ClearTracks]" Padding="8,2"/>
+ </StackPanel>
+
+ <ListBox VerticalAlignment="Stretch" MinHeight="50"
+ Margin="10,10,10,10"
+ dd:DragDrop.DropHandler="{Binding}"
+ dd:DragDrop.IsDragSource="True"
+ dd:DragDrop.IsDropTarget="True"
+ ItemsSource="{Binding BehaviourTracks}"
+ SelectionMode="Extended">
+
+ <ListBox.ItemContainerStyle>
+ <Style TargetType="ListBoxItem">
+ <Setter Property="HorizontalContentAlignment" Value="Stretch" />
+ <Setter Property="Margin" Value="0,0,0,1" />
+ </Style>
+ </ListBox.ItemContainerStyle>
+
+ <ListBox.ContextMenu>
+ <ContextMenu>
+ <MenuItem Header="{x:Static Properties:ResourcesUI.Generic_Clear}" cal:Message.Attach="[Event Click] = [Action ClearTracks]" />
+ </ContextMenu>
+ </ListBox.ContextMenu>
+
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <!-- Marker -->
+ <!--<Image Grid.Column="0" Grid.Row="0"
+ Width="16"
+ Height="16"
+ Margin="8,0,8,0"
+ Source="Images/Movies.png" />-->
+
+ <!-- Settings -->
+ <Grid Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" Margin="0,5,0,5">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <Grid.RowDefinitions>
+ <RowDefinition Height="28" />
+ </Grid.RowDefinitions>
+
+ <!-- Row 1 -->
+ <TextBlock Grid.Column="2" VerticalAlignment="Center" FontWeight="Bold" Text="{x:Static Properties:ResourcesUI.AudioView_Codec}" />
+ <ComboBox Grid.Column="3" Width="100" Height="22" Margin="5,0,5,0">
+ <ComboBox.SelectedItem>
+ <MultiBinding Converter="{StaticResource audioEncoderConverter}">
+ <Binding Path="Encoder" />
+ </MultiBinding>
+ </ComboBox.SelectedItem>
+ <ComboBox.ItemsSource>
+ <MultiBinding Converter="{StaticResource audioEncoderConverter}">
+ <Binding Path="DataContext.AudioEncoders" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
+ <Binding Path="DataContext.Task" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
+ </MultiBinding>
+ </ComboBox.ItemsSource>
+ </ComboBox>
+
+ <ComboBox Grid.Column="4" Height="22" Width="65" HorizontalAlignment="Stretch"
+ ItemsSource="{Binding AudioEncoderRateTypes, Converter={StaticResource audioRateTypeConverter}}"
+ SelectedItem="{Binding EncoderRateType, Converter={StaticResource audioRateTypeConverter}}"
+ Visibility="{Binding IsRateTypeVisible, Converter={StaticResource boolToVisConverter}, ConverterParameter=false}" />
+
+ <ComboBox Grid.Column="5" Width="55" Height="22" Margin="5,0,5,0" ItemsSource="{Binding EncoderQualityValues}"
+ SelectedItem="{Binding Quality}"
+ Visibility="{Binding IsQualityVisible, Converter={StaticResource boolToVisConverter}, ConverterParameter=false}" />
+
+ <ComboBox Grid.Column="5" Width="55" Height="22" Margin="5,0,5,0" ItemsSource="{Binding Bitrates}"
+ SelectedItem="{Binding Bitrate}"
+ Visibility="{Binding IsBitrateVisible, Converter={StaticResource boolToVisConverter}, ConverterParameter=false}" />
+
+ <TextBlock Grid.Row="0" Grid.Column="6" VerticalAlignment="Center" FontWeight="Bold" Text="{x:Static Properties:ResourcesUI.AudioView_Mixdown}"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ <ComboBox Grid.Row="0" Grid.Column="7" Height="22" Width="120" Margin="5,0,5,0" HorizontalAlignment="Stretch"
+ ItemsSource="{Binding DataContext.Mixdowns, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
+ SelectedItem="{Binding MixDown}"
+ DisplayMemberPath="DisplayName"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+
+
+ <TextBlock VerticalAlignment="Center" Grid.Column="8" FontWeight="Bold" Text="{x:Static Properties:ResourcesUI.AudioView_Samplerate}"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ <ComboBox Width="70" Height="22" Margin="5,0,5,0" Grid.Column="9"
+ ItemsSource="{Binding DataContext.SampleRates, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
+ SelectedItem="{Binding SampleRateDisplayValue}"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+
+ <TextBlock VerticalAlignment="Center" FontWeight="Bold" Text="{x:Static Properties:ResourcesUI.AudioView_DRC}" Grid.Column="10"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ <controls:NumberBox Name="drcNumericCtl" Width="45" Margin="5,0,5,0" HorizontalAlignment="Left" Grid.Column="11"
+ Minimum="0" Modulus="0.1" Maximum="4" Number="{Binding DRC, Mode=TwoWay}" UpdateBindingOnTextChange="True" ShowIncrementButtons="True" AllowEmpty="False"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToHiddenVisConverter}, ConverterParameter=true}"/>
+
+ <TextBlock VerticalAlignment="Center" FontWeight="Bold" Text="{x:Static Properties:ResourcesUI.AudioView_Gain}" Grid.Column="12"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToVisConverter}, ConverterParameter=true}" />
+ <controls:NumberBox Name="gainNumericCtl" Width="45" Margin="5,0,5,0" HorizontalAlignment="Left" Grid.Column="13"
+ Minimum="-20" Modulus="1" Maximum="20" Number="{Binding Gain, Mode=TwoWay}" UpdateBindingOnTextChange="True" ShowIncrementButtons="True" AllowEmpty="False"
+ Visibility="{Binding IsPassthru, Converter={StaticResource boolToHiddenVisConverter}, ConverterParameter=true}"/>
+
+ </Grid>
+
+ <!-- Delete -->
+ <StackPanel Grid.Column="2" Grid.Row="0" VerticalAlignment="Center">
+ <Image Width="16" Height="16" Margin="10,0,10,0" Source="Images/delete.png">
+ <i:Interaction.Triggers>
+ <i:EventTrigger EventName="MouseDown">
+ <cal:ActionMessage MethodName="RemoveTrack">
+ <cal:Parameter Value="{Binding}" />
+ </cal:ActionMessage>
+ </i:EventTrigger>
+ </i:Interaction.Triggers>
+ </Image>
+ </StackPanel>
+
+ </Grid>
+
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
</StackPanel>
diff --git a/win/CS/HandBrakeWPF/Views/PopupWindowView.xaml b/win/CS/HandBrakeWPF/Views/PopupWindowView.xaml
index 1008f15fe..f97a41682 100644
--- a/win/CS/HandBrakeWPF/Views/PopupWindowView.xaml
+++ b/win/CS/HandBrakeWPF/Views/PopupWindowView.xaml
@@ -7,8 +7,8 @@
xmlns:properties="clr-namespace:HandBrakeWPF.Properties"
xmlns:converters="clr-namespace:HandBrakeWPF.Converters"
d:DesignHeight="250"
- d:DesignWidth="600"
- MaxHeight="600"
+ d:DesignWidth="650"
+ MaxHeight="650"
Title="{Binding Title}"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
@@ -24,7 +24,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
- <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>