diff --git a/Filtration.Common.Tests/Filtration.Common.Tests.csproj b/Filtration.Common.Tests/Filtration.Common.Tests.csproj
new file mode 100644
index 0000000..ac78317
--- /dev/null
+++ b/Filtration.Common.Tests/Filtration.Common.Tests.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {1E42A658-45C4-4DD9-83C5-2A10728DBDFA}
+ Library
+ Properties
+ Filtration.Common.Tests
+ Filtration.Common.Tests
+ v4.5.1
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Moq.4.2.1506.2515\lib\net40\Moq.dll
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Common.Tests/packages.config b/Filtration.Common.Tests/packages.config
new file mode 100644
index 0000000..74fad4e
--- /dev/null
+++ b/Filtration.Common.Tests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Common/Filtration.Common.csproj b/Filtration.Common/Filtration.Common.csproj
new file mode 100644
index 0000000..1e02c80
--- /dev/null
+++ b/Filtration.Common/Filtration.Common.csproj
@@ -0,0 +1,88 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {8CB44F28-2956-4C2A-9314-72727262EDD4}
+ Library
+ Properties
+ Filtration.Common
+ Filtration.Common
+ v4.5.1
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Castle.Core.3.3.0\lib\net45\Castle.Core.dll
+
+
+ ..\packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll
+
+
+ ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\GalaSoft.MvvmLight.dll
+
+
+ ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\GalaSoft.MvvmLight.Extras.dll
+
+
+ ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\GalaSoft.MvvmLight.Platform.dll
+
+
+ ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll
+
+
+
+
+
+ ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\System.Windows.Interactivity.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {4AAC3BEB-1DC1-483E-9D11-0E9334E80227}
+ Filtration.ObjectModel
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Common/WindsorInstallers/ServicesInstaller.cs b/Filtration.Common/WindsorInstallers/ServicesInstaller.cs
new file mode 100644
index 0000000..fffac19
--- /dev/null
+++ b/Filtration.Common/WindsorInstallers/ServicesInstaller.cs
@@ -0,0 +1,18 @@
+using Castle.MicroKernel.Registration;
+using Castle.MicroKernel.SubSystems.Configuration;
+using Castle.Windsor;
+using Filtration.Common.Services;
+
+namespace Filtration.Common.WindsorInstallers
+{
+ public class ServicesInstaller :IWindsorInstaller
+ {
+ public void Install(IWindsorContainer container, IConfigurationStore store)
+ {
+ container.Register(
+ Component.For()
+ .ImplementedBy()
+ .LifeStyle.Singleton);
+ }
+ }
+}
diff --git a/Filtration.Common/packages.config b/Filtration.Common/packages.config
new file mode 100644
index 0000000..5dfb6bc
--- /dev/null
+++ b/Filtration.Common/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Interface/Filtration.Interface.csproj b/Filtration.Interface/Filtration.Interface.csproj
new file mode 100644
index 0000000..fafafac
--- /dev/null
+++ b/Filtration.Interface/Filtration.Interface.csproj
@@ -0,0 +1,56 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {0F333344-7695-47B2-B0E6-172E4DE74819}
+ Library
+ Properties
+ Filtration.Interface
+ Filtration.Interface
+ v4.5.1
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Interface/IEditableDocument.cs b/Filtration.Interface/IEditableDocument.cs
new file mode 100644
index 0000000..7102812
--- /dev/null
+++ b/Filtration.Interface/IEditableDocument.cs
@@ -0,0 +1,9 @@
+namespace Filtration.Interface
+{
+ public interface IEditableDocument : IDocument
+ {
+ bool IsDirty { get; }
+ void Save();
+ void SaveAs();
+ }
+}
diff --git a/Filtration.ObjectModel.Tests/TestItemFilterBlockGroup.cs b/Filtration.ObjectModel.Tests/TestItemFilterBlockGroup.cs
new file mode 100644
index 0000000..b17eed5
--- /dev/null
+++ b/Filtration.ObjectModel.Tests/TestItemFilterBlockGroup.cs
@@ -0,0 +1,42 @@
+using NUnit.Framework;
+
+namespace Filtration.ObjectModel.Tests
+{
+ [TestFixture]
+ public class TestItemFilterBlockGroup
+ {
+ [Test]
+ public void ToString_ReturnsFullBlockHierarchy()
+ {
+ // Arrange
+ const string ExpectedResult = "Child 1 Block Group - Child 2 Block Group";
+
+ var rootBlockGroup = new ItemFilterBlockGroup("Root Block Group", null);
+ var child1BlockGroup = new ItemFilterBlockGroup("Child 1 Block Group", rootBlockGroup);
+ var child2BlockGroup = new ItemFilterBlockGroup("Child 2 Block Group", child1BlockGroup);
+
+ // Act
+ var result = child2BlockGroup.ToString();
+
+ // Assert
+ Assert.AreEqual(ExpectedResult, result);
+ }
+
+ [Test]
+ public void ToString_AddsTildeForAdvancedBlock()
+ {
+ // Arrange
+ const string ExpectedResult = "~Child 1 Block Group - Child 2 Block Group";
+
+ var rootBlockGroup = new ItemFilterBlockGroup("Root Block Group", null);
+ var child1BlockGroup = new ItemFilterBlockGroup("Child 1 Block Group", rootBlockGroup, true);
+ var child2BlockGroup = new ItemFilterBlockGroup("Child 2 Block Group", child1BlockGroup);
+
+ // Act
+ var result = child2BlockGroup.ToString();
+
+ // Assert
+ Assert.AreEqual(ExpectedResult, result);
+ }
+ }
+}
diff --git a/Filtration.ObjectModel.Tests/packages.config b/Filtration.ObjectModel.Tests/packages.config
new file mode 100644
index 0000000..51f4538
--- /dev/null
+++ b/Filtration.ObjectModel.Tests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.Tests/Translators/TestBlockGroupHierarchyBuilder.cs b/Filtration.Tests/Translators/TestBlockGroupHierarchyBuilder.cs
new file mode 100644
index 0000000..724741e
--- /dev/null
+++ b/Filtration.Tests/Translators/TestBlockGroupHierarchyBuilder.cs
@@ -0,0 +1,121 @@
+using System.Collections.Generic;
+using Filtration.ObjectModel;
+using Filtration.Translators;
+using NUnit.Framework;
+
+namespace Filtration.Tests.Translators
+{
+ [TestFixture]
+ public class TestBlockGroupHierarchyBuilder
+ {
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_ReturnsBlockGroupWithCorrectName()
+ {
+ // Arrange
+ var inputStrings = new List {"Block Group", "Sub Block Group"};
+
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(1, rootBlock.ChildGroups.Count);
+ Assert.AreEqual("Sub Block Group", result.GroupName);
+ }
+
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_SingleBlockGroup()
+ {
+ // Arrange
+ var inputStrings = new List { "Block Group" };
+
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(1, rootBlock.ChildGroups.Count);
+ Assert.AreEqual("Block Group", result.GroupName);
+ }
+
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_AdvancedBlockGroup_MarksBlockGroupAsAdvanced()
+ {
+ // Arrange
+ var inputStrings = new List { "~Block Group" };
+
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(1, rootBlock.ChildGroups.Count);
+ Assert.AreEqual("Block Group", result.GroupName);
+ Assert.AreEqual(true, result.Advanced);
+ }
+
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_AdvancedBlockGroup_ChildrenAreCreatedAsAdvanced()
+ {
+ // Arrange
+ var inputStrings = new List { "~Advanced Block Group", "This should be advanced too" };
+
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(1, rootBlock.ChildGroups.Count);
+ Assert.AreEqual(true, result.Advanced);
+ }
+
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_ExistingAdvancedBlockGroup_SetsParentCorrectly()
+ {
+ // Arrange
+ var inputStrings = new List { "~Block Group" };
+
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var subBlock = new ItemFilterBlockGroup("Block Group", rootBlock, true);
+ rootBlock.ChildGroups.Add(subBlock);
+
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(1, rootBlock.ChildGroups.Count);
+ Assert.AreEqual("Block Group", result.GroupName);
+ Assert.AreEqual(true, result.Advanced);
+ }
+
+ [Test]
+ public void IntegrateStringListIntoBlockGroupHierarchy_MultipleBlockGroups()
+ {
+ // Arrange
+ var rootBlock = new ItemFilterBlockGroup("Root", null);
+ var builder = new BlockGroupHierarchyBuilder();
+
+ // Act
+ var inputStrings = new List { "Block Group" };
+ builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+ inputStrings = new List { "Block Group 2" };
+ builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+ inputStrings = new List { "Block Group", "Sub Block Group" };
+ var result = builder.IntegrateStringListIntoBlockGroupHierarchy(inputStrings, rootBlock);
+
+ // Assert
+ Assert.AreEqual(2, rootBlock.ChildGroups.Count);
+ Assert.AreEqual("Sub Block Group", result.GroupName);
+ }
+ }
+}
diff --git a/Filtration.ThemeEditor.Tests/Filtration.ThemeEditor.Tests.csproj b/Filtration.ThemeEditor.Tests/Filtration.ThemeEditor.Tests.csproj
new file mode 100644
index 0000000..8c3cd58
--- /dev/null
+++ b/Filtration.ThemeEditor.Tests/Filtration.ThemeEditor.Tests.csproj
@@ -0,0 +1,75 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {450AC313-BF25-4BFD-A066-9F39F026FDCF}
+ Library
+ Properties
+ Filtration.ThemeEditor.Tests
+ Filtration.ThemeEditor.Tests
+ v4.5.1
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Moq.4.2.1506.2515\lib\net40\Moq.dll
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {4aac3beb-1dc1-483e-9d11-0e9334e80227}
+ Filtration.ObjectModel
+
+
+ {41b8f5c2-65aa-42f0-a20b-6f95b13a9f48}
+ Filtration.ThemeEditor
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.ThemeEditor.Tests/Models/TestTheme.cs b/Filtration.ThemeEditor.Tests/Models/TestTheme.cs
new file mode 100644
index 0000000..49512c3
--- /dev/null
+++ b/Filtration.ThemeEditor.Tests/Models/TestTheme.cs
@@ -0,0 +1,78 @@
+using System.Windows.Media;
+using Filtration.ObjectModel.BlockItemTypes;
+using Filtration.ObjectModel.Enums;
+using Filtration.ObjectModel.ThemeEditor;
+using NUnit.Framework;
+
+namespace Filtration.ThemeEditor.Tests.Models
+{
+ [TestFixture]
+ public class TestTheme
+ {
+ [Test]
+ public void ComponentExists_ComponentDoesExist_ReturnsTrue()
+ {
+ // Arrange
+ var theme = new Theme();
+
+ var testInputComponentTargetType = ThemeComponentType.TextColor;
+ const string TestInputComponentName = "test";
+
+ theme.AddComponent(testInputComponentTargetType, TestInputComponentName, new Color());
+
+ // Act
+ var result = theme.ComponentExists(testInputComponentTargetType, TestInputComponentName);
+
+ // Assert
+ Assert.AreEqual(true, result);
+ }
+
+ [Test]
+ public void ComponentExists_ComponentDoesNotExist_DifferentNameSameType_ReturnsFalse()
+ {
+ // Arrange
+ var theme = new Theme();
+
+ var testInputComponentTargetType = ThemeComponentType.TextColor;
+ const string TestInputComponentName = "test";
+ theme.AddComponent(testInputComponentTargetType, TestInputComponentName, new Color());
+
+ // Act
+ var result = theme.ComponentExists(testInputComponentTargetType, "blah");
+
+ // Assert
+ Assert.AreEqual(false, result);
+ }
+
+ [Test]
+ public void ComponentExists_ComponentDoesNotExist_DifferentTypeSameName_ReturnsFalse()
+ {
+ // Arrange
+ var theme = new Theme();
+
+ var testInputComponentTargetType = ThemeComponentType.TextColor;
+ const string TestInputComponentName = "test";
+
+ theme.AddComponent(testInputComponentTargetType, TestInputComponentName, new Color());
+
+ // Act
+ var result = theme.ComponentExists(ThemeComponentType.BorderColor, TestInputComponentName);
+
+ // Assert
+ Assert.AreEqual(false, result);
+ }
+
+ [Test]
+ public void ComponentExists_ComponentDoesNotExist_NoComponents_ReturnsFalse()
+ {
+ // Arrange
+ var theme = new Theme();
+
+ // Act
+ var result = theme.ComponentExists(ThemeComponentType.BorderColor, "test");
+
+ // Assert
+ Assert.AreEqual(false, result);
+ }
+ }
+}
diff --git a/Filtration.ThemeEditor.Tests/Services/TestThemeService.cs b/Filtration.ThemeEditor.Tests/Services/TestThemeService.cs
new file mode 100644
index 0000000..68c3938
--- /dev/null
+++ b/Filtration.ThemeEditor.Tests/Services/TestThemeService.cs
@@ -0,0 +1,67 @@
+using System.Windows.Media;
+using Filtration.ObjectModel;
+using Filtration.ObjectModel.BlockItemTypes;
+using Filtration.ObjectModel.Enums;
+using Filtration.ObjectModel.ThemeEditor;
+using Filtration.ThemeEditor.Services;
+using NUnit.Framework;
+
+namespace Filtration.ThemeEditor.Tests.Services
+{
+ [TestFixture]
+ public class TestThemeService
+ {
+ [Test]
+ public void ApplyThemeToScript_SingleBlock_ReplacesColor()
+ {
+ // Arrange
+
+ var testInputBlockItem = new TextColorBlockItem();
+ var testInputBlock = new ItemFilterBlock();
+ testInputBlock.BlockItems.Add(testInputBlockItem);
+ var testInputScript = new ItemFilterScript();
+ testInputScript.ItemFilterBlocks.Add(testInputBlock);
+
+ var testInputTheme = new Theme();
+ var testInputThemeComponentColor = new Color{ R = 255, G = 0, B = 1 };
+ var testInputThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
+ testInputTheme.Components.Add(testInputThemeComponent);
+ testInputBlockItem.ThemeComponent = testInputThemeComponent;
+
+ var service = new ThemeService();
+
+ // Act
+ service.ApplyThemeToScript(testInputTheme, testInputScript);
+
+ // Assert
+ Assert.AreEqual(testInputThemeComponentColor, testInputBlockItem.Color);
+ }
+
+ [Test]
+ public void ApplyThemeToScript_SingleBlockDifferentComponentName_DoesNotReplaceColour()
+ {
+ // Arrange
+
+ var testInputBlockItem = new TextColorBlockItem();
+ var testInputBlock = new ItemFilterBlock();
+ testInputBlock.BlockItems.Add(testInputBlockItem);
+ var testInputScript = new ItemFilterScript();
+ testInputScript.ItemFilterBlocks.Add(testInputBlock);
+
+ var testInputTheme = new Theme();
+ var testInputThemeComponentColor = new Color { R = 255, G = 0, B = 1 };
+ var testInputThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Test Component 1", testInputThemeComponentColor);
+ var testInputBlockItemThemeComponent = new ThemeComponent(ThemeComponentType.TextColor, "Different Component", testInputThemeComponentColor);
+ testInputTheme.Components.Add(testInputThemeComponent);
+ testInputBlockItem.ThemeComponent = testInputBlockItemThemeComponent;
+
+ var service = new ThemeService();
+
+ // Act
+ service.ApplyThemeToScript(testInputTheme, testInputScript);
+
+ // Assert
+ Assert.AreNotEqual(testInputThemeComponentColor, testInputBlockItem.Color);
+ }
+ }
+}
diff --git a/Filtration.ThemeEditor.Tests/packages.config b/Filtration.ThemeEditor.Tests/packages.config
new file mode 100644
index 0000000..74fad4e
--- /dev/null
+++ b/Filtration.ThemeEditor.Tests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Filtration.ThemeEditor/Converters/ThemeComponentTypeToStringConverter.cs b/Filtration.ThemeEditor/Converters/ThemeComponentTypeToStringConverter.cs
new file mode 100644
index 0000000..c71dd6b
--- /dev/null
+++ b/Filtration.ThemeEditor/Converters/ThemeComponentTypeToStringConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using Filtration.ObjectModel.BlockItemTypes;
+using Filtration.ObjectModel.Enums;
+using Filtration.ObjectModel.Extensions;
+
+namespace Filtration.ThemeEditor.Converters
+{
+ public class ThemeComponentTypeToStringConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value == null)
+ {
+ return string.Empty;
+
+ }
+ var type = (ThemeComponentType) value;
+
+ return type.GetAttributeDescription();
+
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Filtration.ThemeEditor/Services/ThemePersistenceService.cs b/Filtration.ThemeEditor/Services/ThemePersistenceService.cs
new file mode 100644
index 0000000..a2543de
--- /dev/null
+++ b/Filtration.ThemeEditor/Services/ThemePersistenceService.cs
@@ -0,0 +1,40 @@
+using System.IO;
+using System.Xml.Serialization;
+using Filtration.ObjectModel.ThemeEditor;
+
+namespace Filtration.ThemeEditor.Services
+{
+ internal interface IThemePersistenceService
+ {
+ Theme LoadTheme(string filePath);
+ void SaveTheme(Theme theme, string filePath);
+ }
+
+ internal class ThemePersistenceService : IThemePersistenceService
+ {
+ public Theme LoadTheme(string filePath)
+ {
+ var xmlSerializer = new XmlSerializer(typeof(Theme));
+
+ Theme loadedTheme;
+
+ using (Stream reader = new FileStream(filePath, FileMode.Open))
+ {
+ loadedTheme = (Theme)xmlSerializer.Deserialize(reader);
+ }
+
+ loadedTheme.FilePath = filePath;
+ return loadedTheme;
+ }
+
+ public void SaveTheme(Theme theme, string filePath)
+ {
+ var xmlSerializer = new XmlSerializer(typeof(Theme));
+
+ using (Stream writer = new FileStream(filePath, FileMode.Create))
+ {
+ xmlSerializer.Serialize(writer, theme);
+ }
+ }
+ }
+}
diff --git a/Filtration.ThemeEditor/ViewModels/ThemeComponentViewModel.cs b/Filtration.ThemeEditor/ViewModels/ThemeComponentViewModel.cs
new file mode 100644
index 0000000..76f2836
--- /dev/null
+++ b/Filtration.ThemeEditor/ViewModels/ThemeComponentViewModel.cs
@@ -0,0 +1,19 @@
+using System.Windows.Media;
+using Filtration.ObjectModel.Enums;
+
+namespace Filtration.ThemeEditor.ViewModels
+{
+ public interface IThemeComponentViewModel
+ {
+ string ComponentName { get; set; }
+ ThemeComponentType ComponentType { get; set; }
+ Color Color { get; set; }
+ }
+
+ public class ThemeComponentViewModel : IThemeComponentViewModel
+ {
+ public string ComponentName { get; set; }
+ public ThemeComponentType ComponentType { get; set; }
+ public Color Color { get; set; }
+ }
+}
diff --git a/Filtration/Converters/HashSignRemovalConverter.cs b/Filtration/Converters/HashSignRemovalConverter.cs
new file mode 100644
index 0000000..d8de2e3
--- /dev/null
+++ b/Filtration/Converters/HashSignRemovalConverter.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace Filtration.Converters
+{
+ internal class HashSignRemovalConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var inputString = (string) value;
+ return inputString.Replace("#", string.Empty);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Filtration/Views/ToolPanes/BlockGroupBrowserView.xaml.cs b/Filtration/Views/ToolPanes/BlockGroupBrowserView.xaml.cs
new file mode 100644
index 0000000..688f384
--- /dev/null
+++ b/Filtration/Views/ToolPanes/BlockGroupBrowserView.xaml.cs
@@ -0,0 +1,24 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Filtration.Views.ToolPanes
+{
+ public partial class BlockGroupBrowserView
+ {
+ public BlockGroupBrowserView()
+ {
+ InitializeComponent();
+ }
+
+ private void BlockGroupCheckBox_Clicked(object sender, RoutedEventArgs e)
+ {
+ // Prevents the user from putting a ThreeState checkbox into the indeterminate state
+ // allowing the indeterminate state to only be set by the object itself.
+ var cb = e.Source as CheckBox;
+ if (cb != null && !cb.IsChecked.HasValue)
+ {
+ cb.IsChecked = false;
+ }
+ }
+ }
+}