i have weird issue i'm convinced bug.
i have 3 usercontrols, folderview, localfolderview, remotefolderview. localfolderview , remotefolderview both inherit folderview , used in 2 other controls, localexplorer , remoteexplorer, respectively.
localexplorer/remoteexplorer have list of strings, bind folderview.
the problem whenever have more 1 instance of localexplorer/remoteexplorer, listbox in folderview both explorers show same items, yet dependency properties controls seemingly different.
the code long i'll try condense as can. currently, believe issue way i'm binding things.
here's control have more 1 instance of exhibits bug:
localexplorer.xaml (remoteexplorer.xaml follows identical pattern):
<usercontrol x:class="localexplorer" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:mynamespace" mc:ignorable="d" width="auto" height="auto" cliptobounds="true" x:name="explorer"> <local:explorerbase path="{binding elementname=explorer, path=path}" orientation="{binding elementname=explorer, path=orientation}"> <local:explorerbase.folderview> <local:localfolderview x:name="folderview" path="{binding path, relativesource={relativesource ancestortype={x:type local:localexplorer}}}"/> </local:explorerbase.folderview> </local:explorerbase> </usercontrol>
localexplorer.xaml.cs (remoteexplorer.xaml.cs follows identical pattern):
public partial class explorer : usercontrol { #region explorer public explorer() { initializecomponent(); } #endregion #region dependency properties public static dependencyproperty pathproperty = dependencyproperty.register("path", typeof(string), typeof(explorer), new frameworkpropertymetadata("", frameworkpropertymetadataoptions.bindstwowaybydefault)); public string path { { return (string)getvalue(pathproperty); } set { setvalue(pathproperty, value); } } #endregion }
next explorerbase, houses ui logic specific explorers:
explorerbase.cs:
public partial class explorerbase : control { public explorerbase() { this.defaultstylekey = typeof(explorerbase); } public override void onapplytemplate() { base.applytemplate(); } public static readonly dependencyproperty folderviewproperty = dependencyproperty.register("folderview", typeof(object), typeof(explorerbase), null); public object folderview { { return getvalue(folderviewproperty); } set { setvalue(folderviewproperty, value); } } public static dependencyproperty pathproperty = dependencyproperty.register("path", typeof(string), typeof(explorerbase), new frameworkpropertymetadata("", frameworkpropertymetadataoptions.bindstwowaybydefault)); public string path { { return (string)getvalue(pathproperty); } set { setvalue(pathproperty, value); } } }
i template using themes/generic.xaml approach:
<style targettype="{x:type imagin.controls:explorerbase}"> <setter property="template"> <setter.value> <controltemplate targettype="imagin.controls:explorerbase"> <contentpresenter content="{templatebinding folderview}"/> </controltemplate> </setter.value> </setter> </style>
and lastly, folderview, believe has bug. folderview base actual controls used, localfolderview , remotefolderview. note, bug occurs regardless of whether or not use both localexplorer , remoteexplorer, or 1 of both. have tested total of 2 instances @ once.
folderview.xaml:
<usercontrol x:class="folderview" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d" height="auto" width="auto" x:name="folderview"> <usercontrol.resources> <booleantovisibilityconverter x:key="booltovisibility" /> <imagin.data:inversebooleantovisibilityconverter x:key="inversebooltovisibility" /> <grid> <listbox x:name="listbox" allowdrop="true" itemssource="{binding path=items, relativesource={relativesource ancestortype={x:type local:folderview}}}" selectionmode="extended" scrollviewer.horizontalscrollbarvisibility="disabled" scrollviewer.verticalscrollbarvisibility="auto" issynchronizedwithcurrentitem="true"> <listbox.itemspanel> <itemspaneltemplate> <wrappanel isitemshost="true"/> </itemspaneltemplate> </listbox.itemspanel> <listbox.resources> <datatemplate datatype="{x:type local:bindablefile}"> <local:thumbnail filepath="{binding path}" ischeckboxenabled="false" tooltip="{binding tooltip}" title="{binding name}" width="auto" height="auto"/> </datatemplate> <datatemplate datatype="{x:type local:bindablefolder}"> <local:thumbnail filepath="{binding path}" ischeckboxenabled="false" tooltip="{binding tooltip}" title="{binding name}" width="auto" height="auto"/> </datatemplate> </listbox.resources> <listbox.itemcontainerstyle> <style targettype="{x:type listboxitem}"> <setter property="isselected" value="{binding isselected, mode=twoway, updatesourcetrigger=propertychanged}" /> </style> </listbox.itemcontainerstyle> </listbox> <datagrid x:name="datagrid" itemssource="{binding items, relativesource={relativesource ancestortype={x:type local:folderview}}}" autogeneratecolumns="false" borderthickness="0" alternationcount="2" gridlinesvisibility="none" headersvisibility="column" canuseraddrows="false" canuserresizecolumns="true" issynchronizedwithcurrentitem="true" allowdrop="true"> </datagrid> </grid> </usercontrol>
folderview.xaml.cs:
public abstract partial class folderview : usercontrol { #region dependencyproperties public static dependencyproperty itemsproperty = dependencyproperty.register("items", typeof(observablecollection<bindablepath>), typeof(folderview), new frameworkpropertymetadata(new observablecollection<bindablepath>(), frameworkpropertymetadataoptions.bindstwowaybydefault)); public observablecollection<bindablepath> items { { return (observablecollection<bindablepath>)getvalue(itemsproperty); } set { setvalue(itemsproperty, value); } } public static dependencyproperty pathproperty = dependencyproperty.register("path", typeof(string), typeof(folderview), new frameworkpropertymetadata("", frameworkpropertymetadataoptions.bindstwowaybydefault, onpathchanged)); public string path { { return (string)getvalue(pathproperty); } set { setvalue(pathproperty, value); } } private static void onpathchanged(dependencyobject object, dependencypropertychangedeventargs e) { folderview folderview = (folderview)object; folderview.refresh(); folderview.searchtextbox.text = string.empty; } #endregion #region methods public virtual void getitems(string path, out list<string> folders, out list<string> files) { folders = default(list<string>); files = default(list<string>); } /// <summary> /// refreshes current path contents. /// </summary> public virtual void refresh() { //used debug property values @ runtime. far values each object instance unique. foreach (propertydescriptor descriptor in typedescriptor.getproperties(this)) { string name = descriptor.name; object value = descriptor.getvalue(this); console.writeline("{0}={1}", name, value); } } /// <summary> /// populates controls actual items via binding. must on ui thread. occurs after <refresh()>. /// </summary> /// <param name="folders"></param> /// <param name="files"></param> public virtual void populate(list<ftplistitem> folders, list<ftplistitem> files) { } public virtual void populate(list<string> folders, list<string> files) { } #endregion #region folderview public folderview() { initializecomponent(); } #endregion }
localfolderview.cs (remotefolderview.cs follows identical pattern):
public sealed class localfolderview : folderview { public override void getitems(string path, out list<string> folders, out list<string> files) { //these own functions folders = directory.getdirectories(path); files = directory.getfiles(path, null); } public override void populate(list<string> folders, list<string> files) { int numfolders = folders.count, numfiles = files.count; this.isempty = numfolders == 0 && numfiles == 0 ? true : false; if (folders == null || files == null || (numfolders == 0 && numfiles == 0)) return; (int j = 0, count = numfolders; j < count; j++) { this.items.add(new bindablefolder(folders[j])); } (int j = 0, count = numfiles; j < count; j++) { this.items.add(new bindablefile(files[j])); } } public override void refresh() { base.refresh(); this.items.clear(); //if directory doesn't exist, don't want enter it. if (!system.io.directory.exists(this.path)) return; list<string> folders = null; list<string> files = null; string currentpath = this.path; backgroundworker worker = new backgroundworker(); worker.dowork += (s, e) => { this.getitems(currentpath, out folders, out files); }; worker.runworkercompleted += (s, e) => { //start populating items var dispatcheroperation = application.current.dispatcher.begininvoke(new action(() => this.populate(folders, files))); }; worker.runworkerasync(); } }
things note:
- the datagrids in folderviews in both instances populate identical items.
- the path property each folderview instance different.
- this occurs after populating second instance items, attempting populate first. if populate first instance first, nothing happens second @ all.
- when both instances populate identical items, mean if populate first, first's items appear in second. , if populate second, second's items appear in first.
- and also, when "populate", mean i'm setting
path
property folderview.
things i've tried:
- changing way bind. instance, instead of binding
binding elementname=explorer, path=property
, changebinding property, relativesource={relativesource ancestortype={x:type local:usercontroltype}}
. - removing
x:name
properties various elements. - dumping folderview's properties/values. example of in source above.
honestly, don't know how else debug. bug or binding logic not logical?
edit
here's how display 2 explorer instances:
<local:localexplorer /> <local:remoteexplorer/>
given both own instances, not see how either mistakenly bind another, considering how nested listboxes in visual tree.
problem in dependency property registration of items property.
public static dependencyproperty itemsproperty = dependencyproperty.register("items", typeof(observablecollection<bindablepath>), typeof(folderview), new frameworkpropertymetadata(new observablecollection<bindablepath>(), frameworkpropertymetadataoptions.bindstwowaybydefault));
as can see registration static , registered on type , not on instance. since default value provided new observablecollection<bindablepath>()
, same instance shared across instances of folderview. that's why, whenever new item added, shown in instances because in nutshell, items property referring same instance.
as thumb rule, should avoid providing new instance reference types during dependency property registration.
solution:
make default value null , instead initialize new instance constructor of folderview (per instance).
new frameworkpropertymetadata(null,frameworkpropertymetadataoptions.bindstwowaybydefault));
Comments
Post a Comment