XAML bind BitmapImage ViewModel property

user572844 picture user572844 · Jan 16, 2011 · Viewed 12.1k times · Source

I have problem with update listbox from view model class. I use Caliburn Micro framework. My scenario is here:

I bind property of type bindableCollection on listbox:

Code from view model:

private BindableCollection<UserInfo> _friends;

public BindableCollection<UserInfo> Friends
{
    get { return _friends; }
    set
    {
        _friends= value;
        NotifyOfPropertyChange(()=>Friends);
    }
}

In view model I create a fake service method which return new fresh data as List and with this data I update a property Friends which is bind on listbox.

I call fake service method in dispatcher timer tick event every 3 seconds.

 private static UserInfo FakeUser()
        {
            var user = new UserInfo
            {
                Age = "16",
                Emphasis = true,
                IdUser = "11542",
                IsBlocked = false,
                IsFriend = true,
                LocationInfo = new Location
                {
                    CityName = "TN",
                    IdCity = 123456,
                    IdRegion = 1246,
                    RegionName = "TN",
                },
                StatusInfo = new Status
                {
                    IdChat = 12,
                    IsLogged = true,
                    LastLogin = "153151",
                    IsChating = true,
                    RoomName = "Car",
                },
                ProjectStatusInfo = new ProjectStatus(),
                IsIamFriend = true,
                PlusInfo = new Plus(),
                ProfilePhoto = new BitmapImage(new Uri("http://pokec.azet.sk/vanes90?i9=1f104a294997", UriKind.RelativeOrAbsolute))
            };    
            return user;
        }

        private static IEnumerable<UserInfo> GetFakeFriends()
        {
            var list = new List<UserInfo>();

            for (int i = 0; i < 20; i++)
            {
                list.Add(FakeUser());
            }

            return list;
        }

        private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
        {
            if (_isExecuting)
                return;
            _isExecuting = true;
            new System.Threading.Tasks.Task(() =>
            {
                var freshFriends = GetFakeFriends();

                Execute.OnUIThread((System.Action)(() =>
                {
                    Friends.Clear();
                    foreach (var freshFriend in freshFriends)
                    {
                        Friends.Add(freshFriend);

                    }
                }));
            }).Start();

            _isExecuting = false;
        }

    }

If I don't apply any style on listbox, it works good.

View:

<Grid>
    <ListBox Name="Friends"
             Grid.Row="2" 
             Margin="4,4,4,4">
    </ListBox>
</Grid>

If I apply some style in which I bind property ProfilePhoto (typeof BitmapeImage) from UserInfo on listbox.

Style is here:

        <Style x:Key="friendsListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Grid Name="RootLayout">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*"></ColumnDefinition>
                                <ColumnDefinition Width="*"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="60"></RowDefinition>
                            </Grid.RowDefinitions>
                            <Image Margin="4,4,4,2" Source="{Binding Path=ProfilePhoto}" Grid.Column="0"/>
                        </Grid>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

I get this error:

Must create DependencySource on same Thread as the DependencyObject.

   at System.Windows.Markup.XamlReader.RewrapException(Exception e, Uri baseUri)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
   at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
   at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
   at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
   at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
   at System.Windows.FrameworkElement.ApplyTemplate()
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)

If I make another style on listbox / listbox item, in which I bind only string or bool properties it works good.

I have problem only if bind bitmapImage property.

BitmapImage property is init as:

ProfilePhoto = new BitmapImage(new Uri("http://pokec.azet.sk/vanes90?i9=1f104a294997", UriKind.RelativeOrAbsolute))

URI is url of picture or path to the file.

What is wrong? Thank for help and advice.

Style is good, it work only if I don't refresh data with method call in another thread.

Answer

Kent Boogaart picture Kent Boogaart · Jan 16, 2011

If you're creating the BitmapImage on any thread other than the UI thread, that would explain this issue. You can freeze the BitmapImage to ensure it is accessible from any thread:

var bitmapImage = new BitmapImage(...);
bitmapImage.Freeze();