C#WPF如何从另一个线程访问UIElements

时间:2017-09-29 17:23:53

标签: c# wpf multithreading user-interface

Simplifited solution graph. 当我尝试将ListViewItem添加到浏览器时,它会抛出System.InvalidOperationException:调用者无法访问对象,因为它属于另一个线程。

//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddItems(string ID)
{
    Browser.Dispatcher.Invoke(() =>
    {
        IEnumerable<ListViewItem> Items = Helper.GetCached(ID);

        lock (((ICollection)Items).SyncRoot)
        {
            if (Items != null)
            {
                foreach (var Item in Items)
                {
                    Browser.Items.Add(Item);//System.InvalidOperationException
                }
            }
        }

    });
}

帮助实施:

internal static class Helper
{
    static object @lock = new object();

    static readonly Dictionary<string, List<ListViewItem>> Cached = new Dictionary<string, List<ListViewItem>>();
    static readonly List<HintFilter> HintFilters = new List<HintFilter>();

    internal static void AddHintFilterService(HintFilter Filter)
    {
        lock (((ICollection)HintFilters).SyncRoot)
        {
            if (!HintFilters.Contains(Filter))
                HintFilters.Add(Filter);
        }
    }

    internal static void ClearHintFilters()
    {
        lock (((ICollection)HintFilters).SyncRoot)
        {
            HintFilters.Clear();
        }
    }


    #region Services
    static readonly List<IService> Services = new List<IHintParserService>();

    internal static void ServicesParserEntry()
    {
        System.Timers.Timer ServicesUpdate = new System.Timers.Timer(1000)
        {
            AutoReset = true,
            Enabled = true
        };

        ServicesUpdate.Elapsed += ServicesUpdate_Elapsed;
        ServicesUpdate.Start();
    }

    static void ServicesUpdate_Elapsed(object sender, ElapsedEventArgs e)
    {
        lock (((ICollection)Services).SyncRoot)
        {
            if (!Services.Any())
                return;

            foreach (var Service in Services)
            {
                if (Service.NeedReparse())
                    ThreadPool.QueueUserWorkItem(Parse, Service);
            }
        }

    }

    internal static List<ListViewItem> GetCached(string ID)
    {
        lock (((IDictionary)Cached).SyncRoot)
        {
            if (Cached.Keys.Contains(ID))
                return Cached[ID];
            else
                return null;
        }
    }

    internal static void ClearCached()
    {
        lock (((IDictionary)Cached).SyncRoot)
        {
            Cached.Clear();
        }
    }

    internal static void AddService(IService Service)
    {
        lock (((ICollection)Services).SyncRoot)
        {
            if (!Services.Contains(Service))
                Services.Add(Service);
        }
    }

    internal static void RemoveService(IService Service)
    {
        lock (((ICollection)Services).SyncRoot)
        {
            if (!Services.Contains(Service))
                throw new InvalidOperationException("Unable to remove service:" + Service.ID());


            Services.Remove(Service);
        }
    }

    static object ParseLock = new object();
    static void Parse(object O)
    {
        lock (ParseLock)
        {
            var Service = O as IService;

            if (!Service.NeedReparse())
                return;

            lock (((IDictionary)Cached).SyncRoot)
            {
                if (Cached.Keys.Contains(Service.ID()))
                    Cached.Remove(Service.ID());//clear
            }

            var Hints = Service.Parse();

            Cache(Hints, Service.ID());
        }
    }

    static void Cache(List<Hint> Hints, string ID)
    {
        Thread CacheThread = new Thread(() =>
        {
            //Thread.CurrentThread.SetApartmentState(ApartmentState.STA);

            List<ListViewItem> CachedHints = new List<ListViewItem>();

            foreach (Hint H in Hints)
            {
                CachedHints.Add(Cache(H));
            }

            lock (((IDictionary)Cached).SyncRoot)
            {
                Cached.Add(ID, CachedHints);
            }
        });

        CacheThread.SetApartmentState(ApartmentState.STA);//MTA can't create UI elements
        CacheThread.Start();
        CacheThread.Join();
    }

    internal static ListViewItem Cache(Hint Hint)
    {
        ListViewItem Item = new ListViewItem()
        {
            MaxHeight = 40,
            MinHeight = 0,
            Margin = new Thickness(0)
        };
        WrapPanel Wrapper = new WrapPanel();

        BitmapImage FilterContent = null;

        if (!string.IsNullOrEmpty(Hint.GroupName))
        {
            var HintFilter = HintFilters.FirstOrDefault(f => f.GroupName == Hint.GroupName);

            if (HintFilter != null)
            {
                FilterContent = HintFilter.Image;
            }
        }

        if (FilterContent != null)
        {
            var Img = new Image()
            {
                Width = 30,
                Height = 24,
                Source = FilterContent
            };
            Wrapper.Children.Add(Img);
        }

        TextBlock Text = new TextBlock()
        {
            Text = Hint.Display,
            VerticalAlignment = VerticalAlignment.Bottom,
            Margin = new Thickness(0),
            Height = 24
        };

        Wrapper.Children.Add(Text);
        Wrapper.Margin = new Thickness(0);
        Wrapper.MinHeight = 24;
        Item.Content = Wrapper;

        return Item;
    }

    #endregion
}

我的问题是:如何正确引用图形? Mabey我应该尝试复制元素并将它们用于浏览器。解决方案并不小,因此很难提供所有细节。

调用堆栈:

  • [插件]请求元素更新

  • [PluginWrapperMethod]

  • [CurrentMethod]

0 个答案:

没有答案