IProperty is one more concept that is used quite often throughout the ReSharper API. Simply put, an IProperty object is able to track the changes, validate new values being assigned, and notify about the changes. IProperty has much in common with signals.
For example, the SomeIntProperty parameter of the PropertyProvider class is important for the work of PropertyTester. Once the parameter is updated, PropertyTester is notified about the new value:
public class PropertyProvider
{
public IProperty<int> SomeIntProperty;
public PropertyProvider(Lifetime lifetime)
{
SomeIntProperty = new Property<int>(lifetime, "PropertyProvider.SomeIntProperty");
}
}
public class PropertyTester
{
private readonly PropertyProvider _propertyProvider;
public PropertyTester(Lifetime lifetime, PropertyProvider propertyProvider)
{
_propertyProvider = propertyProvider;
_propertyProvider.SomeIntProperty.BeforeChange.Advise(lifetime, args =>
{
if (args.New < 0)
args.Cancel = true;
});
_propertyProvider.SomeIntProperty.Change.Advise(lifetime,
val => MessageBox.ShowInfo($"New property value is {val}"));
}
public void ChangePropertyValue(int value)
{
_propertyProvider.SomeIntProperty.Value = value;
}
}
One more obvious example of how IProperty can be used is global options: once a user changes an option on the Options page, the plugin gets instantly notified about the change.
Text = new Property<string>(lifetime, "OptionsExampleViewModel.Text");
var checkMeOption =
settingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide)
.GetValueProperty(lifetime, (MySettingsKey key) => key.CheckMe);
checkMeOption.Change.Advise_HasNew(lifetime, v =>
{
Text.Value = v.New ? "checked" : "not checked";
});
IProperty implements INotifyPropertyChanged out of the box, so, it is able to participate in WPF data binding. For example, we can show the current value of an IProperty on a WPF control. Let's take a look at how to do this based on the example from the previous section. Say, we want to display the value of the CheckMe setting (located in ReSharper | Options...). This is how the view model looks like:
public class OptionsPageViewModel: AAutomation
{
public IProperty<string> Text { get; set; }
public OptionsPageViewModel(Lifetime lifetime, ISettingsStore settingsStore)
{
Text = new Property<string>(lifetime, "OptionsExampleViewModel.Text");
var checkMeOption =
settingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide)
.GetValueProperty(lifetime, (MySettingsKey key) => key.CheckMe);
checkMeOption.Change.Advise_HasNew(lifetime, v =>
{
Text.Value = v.New ? "checked" : "not checked";
});
}
}
The code behind the view looks like follows:
[View]
public partial class OptionsPageView : IView<OptionsPageViewModel>
{
public new string Name => "Options";
public OptionsPageView()
{
InitializeComponent();
}
}
You can organize the data flow between two properties. The properties can be of the same type or of different types. In the latter case, you should write a type converter. For example, we have two string properties: the first one provides some string; the second one takes this string and applies to it Title Capitalization:
public class PropertyFlow
{
public IProperty<string> SourceProperty { get; set; }
public IProperty<string> TargetProperty { get; set; }
public PropertyFlow(Lifetime lifetime)
{
SourceProperty = new Property<string>(lifetime, "sourceProperty");
TargetProperty = new Property<string>(lifetime, "targetProperty");
SourceProperty.FlowChangesInto(lifetime, TargetProperty, s =>
{
if (string.IsNullOrEmpty(s))
return "";
var textInfo = new CultureInfo("en-US", false).TextInfo;
s = textInfo.ToTitleCase(s);
return s;
});
}
}
Color Themes
The classes responsible for working with color themes extensively use IProperty. For more details, refer to Work with Color Themes.