Skip to content

Commit

Permalink
fix(itemscontrol): Bind DisplayMemberPath target
Browse files Browse the repository at this point in the history
Apply a binding rather than a one-time set to the property targeted by DisplayMemberPath. This ensures items update correctly if the property value should change.
  • Loading branch information
davidjohnoliver committed Aug 9, 2021
1 parent d682d03 commit 4102f1b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
Expand Up @@ -1350,6 +1350,35 @@ public async Task When_Selection_Events()
list.SelectedIndex = 1;
}

[TestMethod]
public async Task When_DisplayMemberPath_Property_Changed()
{
var itemsSource = (new[] { "aaa", "bbb", "ccc", "ddd" }).Select(s => new When_DisplayMemberPath_Property_Changed_DataContext { Display = s }).ToArray();

var SUT = new ListView()
{
ItemContainerStyle = BasicContainerStyle,
DisplayMemberPath = "Display",
ItemsSource = itemsSource
};

WindowHelper.WindowContent = SUT;
await WindowHelper.WaitForLoaded(SUT);

var secondContainer = await WindowHelper.WaitForNonNull(() => SUT.ContainerFromIndex(1) as ListViewItem);
await WindowHelper.WaitForLoaded(secondContainer);

var tb = secondContainer.FindFirstChild<TextBlock>();
Assert.AreEqual("bbb", tb.Text);

foreach (var item in itemsSource)
{
item.Display = item.Display.ToUpperInvariant();
}

await WindowHelper.WaitForResultEqual("BBB", () => tb.Text);
}

private bool ApproxEquals(double value1, double value2) => Math.Abs(value1 - value2) <= 2;

private class When_Removed_From_Tree_And_Selection_TwoWay_Bound_DataContext : global::System.ComponentModel.INotifyPropertyChanged
Expand Down Expand Up @@ -1413,6 +1442,26 @@ protected void RaiseAndSetIfChanged<T>(ref T backingField, T value, [CallerMembe
}
}
}

private class When_DisplayMemberPath_Property_Changed_DataContext : INotifyPropertyChanged
{
private string _display;

public event PropertyChangedEventHandler PropertyChanged;

public string Display
{
get => _display;
set
{
if (value != _display)
{
_display = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Display)));
}
}
}
}
}

public partial class OnItemsChangedListView : ListView
Expand Down
16 changes: 9 additions & 7 deletions src/Uno.UI/UI/Xaml/Controls/ItemsControl/ItemsControl.cs
Expand Up @@ -1046,18 +1046,20 @@ protected virtual void PrepareContainerForItemOverride(DependencyObject element,

var styleFromItemsControl = ItemContainerStyle ?? ItemContainerStyleSelector?.SelectStyle(item, element);

object GetContent()
void SetContent(UIElement container, DependencyProperty contentProperty)
{
var displayMemberPath = DisplayMemberPath;
if (string.IsNullOrEmpty(displayMemberPath))
{
return item;
container.SetValue(contentProperty, item);
}
else
{
// TODO: Cache the BindingPath
var b = new BindingPath(displayMemberPath, item) { DataContext = item };
return b.Value;
container.SetBinding(contentProperty, new Binding
{
Path = displayMemberPath,
Source = item
});
}
}

Expand All @@ -1078,7 +1080,7 @@ object GetContent()

if (!isOwnContainer)
{
containerAsContentPresenter.Content = GetContent();
SetContent(containerAsContentPresenter, ContentPresenter.ContentProperty);
}
}
else if (element is ContentControl containerAsContentControl)
Expand All @@ -1101,7 +1103,7 @@ object GetContent()
// Set the datacontext first, then the binding.
// This avoids the inner content to go through a partial content being
// the result of the fallback value of the binding set below.
containerAsContentControl.DataContext = GetContent();
SetContent(containerAsContentControl, ContentControl.DataContextProperty);

if (!containerAsContentControl.IsContainerFromTemplateRoot && containerAsContentControl.GetBindingExpression(ContentControl.ContentProperty) == null)
{
Expand Down

0 comments on commit 4102f1b

Please sign in to comment.