Skip to content

Commit c883d71

Browse files
committed
Allow to bind to converter parameter
1 parent 563f313 commit c883d71

File tree

3 files changed

+94
-9
lines changed

3 files changed

+94
-9
lines changed

src/Avalonia.Base/Data/Core/BindingExpression.cs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Globalization;
3-
using System.Reactive.Linq;
43
using System.Reactive.Subjects;
54
using Avalonia.Data.Converters;
65
using Avalonia.Logging;
@@ -20,8 +19,10 @@ public class BindingExpression : LightweightObservableBase<object?>, ISubject<ob
2019
private readonly object? _fallbackValue;
2120
private readonly object? _targetNullValue;
2221
private readonly BindingPriority _priority;
23-
InnerListener? _innerListener;
24-
WeakReference<object>? _value;
22+
private readonly IDisposable? _converterParameterSubscription;
23+
private WeakReference<object?>? _converterParameter;
24+
private InnerListener? _innerListener;
25+
private WeakReference<object>? _value;
2526

2627
/// <summary>
2728
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@@ -84,11 +85,36 @@ public BindingExpression(
8485

8586
_inner = inner;
8687
_targetType = targetType;
87-
Converter = converter;
88-
ConverterParameter = converterParameter;
8988
_fallbackValue = fallbackValue;
9089
_targetNullValue = targetNullValue;
9190
_priority = priority;
91+
92+
Converter = converter;
93+
94+
if (converterParameter is InstancedBinding binding)
95+
{
96+
if (binding.Mode == BindingMode.TwoWay)
97+
{
98+
throw new InvalidOperationException("Two way binding to converter parameter is not supported");
99+
}
100+
101+
_converterParameterSubscription = binding.Observable?.Subscribe(cp =>
102+
{
103+
_converterParameter = new WeakReference<object?>(cp);
104+
105+
if (_value != null &&
106+
_value.TryGetTarget(out var currentValue))
107+
{
108+
//Force update binding to use new converter parameter
109+
_innerListener?.OnNext(currentValue);
110+
}
111+
112+
});
113+
}
114+
else
115+
{
116+
_converterParameter = new WeakReference<object?>(converterParameter);
117+
}
92118
}
93119

94120
/// <summary>
@@ -99,7 +125,19 @@ public BindingExpression(
99125
/// <summary>
100126
/// Gets a parameter to pass to <see cref="Converter"/>.
101127
/// </summary>
102-
public object? ConverterParameter { get; }
128+
public object? ConverterParameter
129+
{
130+
get
131+
{
132+
if (_converterParameter != null &&
133+
_converterParameter.TryGetTarget(out var cp))
134+
{
135+
return cp;
136+
}
137+
138+
return null;
139+
}
140+
}
103141

104142
/// <inheritdoc/>
105143
string? IDescription.Description => _inner.Expression;
@@ -182,8 +220,17 @@ public void OnNext(object? value)
182220
}
183221
}
184222

185-
protected override void Initialize() => _innerListener = new InnerListener(this);
186-
protected override void Deinitialize() => _innerListener?.Dispose();
223+
protected override void Initialize()
224+
{
225+
_innerListener = new InnerListener(this);
226+
}
227+
228+
protected override void Deinitialize()
229+
{
230+
_innerListener?.Dispose();
231+
232+
_converterParameterSubscription?.Dispose();
233+
}
187234

188235
protected override void Subscribed(IObserver<object> observer, bool first)
189236
{

src/Markup/Avalonia.Markup/Data/BindingBase.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,23 @@ protected abstract ExpressionObserver CreateExpressionObserver(
118118
converter = new StringFormatValueConverter(StringFormat!, converter);
119119
}
120120

121+
var converterParameter = ConverterParameter;
122+
123+
if (ConverterParameter is BindingBase converterParameterBinding)
124+
{
125+
converterParameter = converterParameterBinding.Initiate(
126+
target,
127+
null,
128+
anchor);
129+
}
130+
121131
var subject = new BindingExpression(
122132
observer,
123133
targetType,
124134
fallback,
125135
TargetNullValue,
126136
converter ?? DefaultValueConverter.Instance,
127-
ConverterParameter,
137+
converterParameter,
128138
Priority);
129139

130140
return new InstancedBinding(subject, Mode, Priority);

tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,34 @@ public void Should_Pass_ConverterParameter_To_Supplied_Converter()
372372
Assert.Same("foo", ((BindingExpression)result).ConverterParameter);
373373
}
374374

375+
376+
[Fact]
377+
public void Should_Pass_ConverterParameter_Binding_To_Supplied_Converter()
378+
{
379+
var target = new TextBlock();
380+
var converter = new Mock<IValueConverter>();
381+
382+
var source = new Source { Foo = "foo" };
383+
384+
var binding = new Binding
385+
{
386+
Converter = converter.Object,
387+
ConverterParameter = new Binding(nameof(source.Foo))
388+
{
389+
Source = source
390+
},
391+
Path = "Bar",
392+
};
393+
394+
var result = binding.Initiate(target, TextBox.TextProperty).Subject;
395+
396+
Assert.Same("foo", ((BindingExpression)result).ConverterParameter);
397+
398+
source.Foo = "foo2";
399+
400+
Assert.Same("foo2", ((BindingExpression)result).ConverterParameter);
401+
}
402+
375403
[Fact]
376404
public void Should_Return_FallbackValue_When_Path_Not_Resolved()
377405
{

0 commit comments

Comments
 (0)