Cannot drag activities from WPF ListBox into WorkflowView - c#

I'm trying to host the Workflow Designer in a WPF application. The WorkflowView control is hosted under a WindowsFormsHost control. I've managed to load workflows onto the designer which is successfully linked to a PropertyGrid, also hosted in another WindowsFormsHost.
WorkflowView workflowView = rootDesigner.GetView(ViewTechnology.Default) as WorkflowView;
window.WorkflowViewHost.Child = workflowView;
The majority of the rehosting code is the same as in http://msdn.microsoft.com/en-us/library/aa480213.aspx.
I've created a custom Toolbox using a ListBox WPF control bound to a list of ToolboxItems.
<ListBox Grid.Row="1" Margin="0 0 0 4" BorderThickness="1" BorderBrush="DarkGray" ItemsSource="{Binding Path=ToolboxItems}" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown" AllowDrop="True">
<ListBox.Resources>
<vw:BitmapSourceTypeConverter x:Key="BitmapSourceConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type dd:ToolboxItem}">
<StackPanel Orientation="Horizontal" Margin="3">
<Image Source="{Binding Path=Bitmap, Converter={StaticResource BitmapSourceConverter}}" Height="16" Width="16" Margin="0 0 3 0" />
<TextBlock Text="{Binding Path=DisplayName}" FontSize="14" Height="16" VerticalAlignment="Center" />
<StackPanel.ToolTip>
<TextBlock Text="{Binding Path=Description}" />
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the ListBox_PreviewMouseLeftButtonDown handler:
private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListBox parent = (ListBox)sender;
UIElement dataContainer;
//get the ToolboxItem for the selected item
object data = GetObjectDataFromPoint(parent, e.GetPosition(parent), out dataContainer);
//if the data is not null then start the drag drop operation
if (data != null)
{
DataObject dataObject = new DataObject();
dataObject.SetData(typeof(ToolboxItem), data);
DragDrop.DoDragDrop(parent, dataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
}
With that setup, I'm unable to drag any item from my custom Toolbox onto the designer. The cursor is always displayed as "No" anywhere on the designer.
I've been trying to find anything about this on the net for half a day now and I really hope some can help me here.
Any feedback is much appreciated. Thank you!
Carlos

This might sound stupid as my system is shutting down. :) But can you check your WorkflowView for whether AllowDrop is set? Have you handled the DragEnter event?

Finally got Drag and Drop working. There were three things that needed doing, for whatever reason WorkflowView has:
1.) I had to use System.Windows.Forms.DataObject instead of System.Windows.DataObject when serializing the ToolboxItem when doing DragDrop.
private void ListBox_MouseDownHandler(object sender, MouseButtonEventArgs e)
{
ListBox parent = (ListBox)sender;
//get the object source for the selected item
object data = GetObjectDataFromPoint(parent, e.GetPosition(parent));
//if the data is not null then start the drag drop operation
if (data != null)
{
System.Windows.Forms.DataObject dataObject = new System.Windows.Forms.DataObject();
dataObject.SetData(typeof(ToolboxItem), data as ToolboxItem);
DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
}
2.) DragDrop.DoDragDrop source must be set to the IToolboxService set in the IDesignerHost. The control holding the ListBox implements IToolboxService.
// "this" points to ListBox's parent which implements IToolboxService.
DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Move | DragDropEffects.Copy);
3.) The ListBox should be bound to a list of ToolboxItems returned by the following helper method, passing it the Type of the activities to show in the tool box:
...
this.ToolboxItems = new ToolboxItem[]
{
GetToolboxItem(typeof(IfElseActivity))
};
...
internal static ToolboxItem GetToolboxItem(Type toolType)
{
if (toolType == null)
throw new ArgumentNullException("toolType");
ToolboxItem item = null;
if ((toolType.IsPublic || toolType.IsNestedPublic) && typeof(IComponent).IsAssignableFrom(toolType) && !toolType.IsAbstract)
{
ToolboxItemAttribute toolboxItemAttribute = (ToolboxItemAttribute)TypeDescriptor.GetAttributes(toolType)[typeof(ToolboxItemAttribute)];
if (toolboxItemAttribute != null && !toolboxItemAttribute.IsDefaultAttribute())
{
Type itemType = toolboxItemAttribute.ToolboxItemType;
if (itemType != null)
{
// First, try to find a constructor with Type as a parameter. If that
// fails, try the default constructor.
ConstructorInfo ctor = itemType.GetConstructor(new Type[] { typeof(Type) });
if (ctor != null)
{
item = (ToolboxItem)ctor.Invoke(new object[] { toolType });
}
else
{
ctor = itemType.GetConstructor(new Type[0]);
if (ctor != null)
{
item = (ToolboxItem)ctor.Invoke(new object[0]);
item.Initialize(toolType);
}
}
}
}
else if (!toolboxItemAttribute.Equals(ToolboxItemAttribute.None))
{
item = new ToolboxItem(toolType);
}
}
else if (typeof(ToolboxItem).IsAssignableFrom(toolType))
{
// if the type *is* a toolboxitem, just create it..
//
try
{
item = (ToolboxItem)Activator.CreateInstance(toolType, true);
}
catch
{
}
}
return item;
}
GetToolboxItem method is from http://msdn.microsoft.com/en-us/library/aa480213.aspx source, in the ToolboxService class.
Cheers,
Carlos

Related

Making controls transparent to mouse interaction in WPF

I am trying to make a WPF listbox replicate the behaviour of an old Winforms CheckedListBox, or the checked list box used in e.g. AnkhSVN. I have seen examples that show how to use a DataTemplate to create a check box for every time (e.g. Wpf CheckedListbox - how to get selected item), but this feels very clunky compared to the winforms control:
The logic of "If the user changes a check state, ensure that check state changes for all selected items" is not present by default.
The hit area to change an item from checked to unchecked is the box /and/ the title, rather than just the box as in Winforms
I can handle the first issue by adding a listener to the PropertyChanged event on each item in the bound collection, and if IsChecked changes, then set IsChecked to the same value for all currently selected items.
However, I cannot find a good solution to the second issue. By splitting the DataTemplate into a Checkbox with no title, and a TextBlock with the title, I can reduce the hit area to change the check state to only the desired square. However, all mouse interaction which hits the TextBlock does nothing - I would like it to behave the same as in a normal listbox, or in the dead space outside of the Textblock: If the user is holding shift, then select everything up to and including this item, if not, then clear the selection and select only this item. I could try to implement something where I handled Mouse* events on the TextBlock, but that seems brittle and inelegant - I'd be trying to recreate the exact behaviour of the ListBox, rather than passing events to the listbox.
Here's what I've got currently:
XAML:
<ListBox x:Name="_lstReceivers" SelectionMode="Extended" Margin="10,41,6,15"
ItemsSource="{Binding Receivers}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" IsHitTestVisible="True"/>
<TextBlock Text="{Binding Item}" Background="{x:Null}" IsHitTestVisible="False"/><!--Attempt to make it pass mouse events through. Doesn't work. Yuk.-->
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to get the "Change all checks at the same time" logic (removed some error handling for clarity):
private void ListBoxItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var item = sender as CheckableItem<Receiver>;
if (item == null)
return;
if (e.PropertyName == nameof(CheckableItem<Receiver>.IsChecked))
{
bool newVal = item.IsChecked;
foreach (CheckableItem<Receiver> changeItem in _lstReceivers.SelectedItems)
{
changeItem.IsChecked = newVal;
}
}
}
By trying various combinations of Background = "{x:Null}" and IsHitTestVisible="False", I did manage to get the entire item to not respond to mouse click events - but I could not make it have only the Checkbox respond to mouse events, while everything else is passed to the ListBox for proper selection processing.
Any help would be greatly appreciated.
Answering my own question again.
Well, I couldn't find a clean way to do it, so I ended up setting the ListBoxItem to have IsHitTestVisible="False", and manually tracing mouse events using PreviewMouseDown.
Final code:
XAML:
<ListBox x:Name="_lstReceivers" SelectionMode="Extended" Margin="10,41,6,15"
ItemsSource="{Binding Receivers}" PreviewMouseDown="_lstReceivers_MouseDown">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding IsSelected}" IsHitTestVisible="False">
<StackPanel Orientation="Horizontal" Background="{x:Null}">
<CheckBox IsChecked="{Binding IsChecked}" IsHitTestVisible="True" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"/>
<TextBlock Text="{Binding Item}" Background="{x:Null}" IsHitTestVisible="False"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind:
//Logic to handle allowing the user to click the checkbox, but have everywhere else respond to normal listbox logic.
private void _lstReceivers_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Visual curControl = _lstReceivers as Visual;
ListBoxItem testItem = null;
//Allow normal selection logic to take place if the user is holding shift or ctrl
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl) || Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
return;
//Find the control which the user clicked on. We require the relevant ListBoxItem too, so we can't use VisualTreeHelper.HitTest (Or it wouldn't be much use)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(curControl); i++)
{
var testControl = (Visual)VisualTreeHelper.GetChild(curControl, i);
var rect = VisualTreeHelper.GetDescendantBounds(testControl);
var pos = e.GetPosition((IInputElement)curControl) - VisualTreeHelper.GetOffset(testControl);
if (!rect.Contains(pos))
continue;
else
{
//There are multiple ListBoxItems in the tree we walk. Only take the first - and use it to remember the IsSelected property.
if (testItem == null && testControl is ListBoxItem)
testItem = testControl as ListBoxItem;
//If we hit a checkbox, handle it here
if (testControl is CheckBox)
{
//If the user has hit the checkbox of an unselected item, then only change the item they have hit.
if (!testItem.IsSelected)
dontChangeChecks++;
((CheckBox)testControl).IsChecked = !((CheckBox)testControl).IsChecked;
//If the user has hit the checkbox of a selected item, ensure that the entire selection is maintained (prevent normal selection logic).
if (testItem.IsSelected)
e.Handled = true;
else
dontChangeChecks--;
return;
}
//Like recursion, but cheaper:
curControl = testControl;
i = -1;
}
}
}
//Guard variable
int dontChangeChecks = 0;
//Logic to have all selected listbox items change at the same time
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (dontChangeChecks > 0)
return;
var newVal = ((CheckBox)sender).IsChecked;
dontChangeChecks++;
try
{
//This could be improved by making it more generic.
foreach (CheckableItem<Receiver> item in _lstReceivers.SelectedItems)
{
item.IsChecked = newVal.Value;
}
}
finally
{
dontChangeChecks--;
}
}
This solution works, but I don't like the coupling it introduces between my code and the exact behaviour of the ListBox implementation:
Checking the Keyboard state
It won't handle dragging if the user starts dragging inside a checkbox
It should happen on mouseup, not mousedown. But it's close enough for my needs.
PS: The bound class, even though it's irrelevant and obvious what it would have:
public class CheckableItem<T> : INotifyPropertyChanged
{
public T Item { get; set; }
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected == value)
return;
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
private bool _checked;
public bool IsChecked
{
get => _checked;
set
{
if (_checked == value)
return;
_checked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Remove data from a bound list C#

i am currently trying to remove items from a bound list.
Here is where it is bound in the xaml.
<ListBox Height="362" HorizontalAlignment="Left" Margin="6,245,0,0" Name="lstHoldCategories" VerticalAlignment="Top" Width="462" SelectionChanged="list_SelectionChanged_1" BorderThickness="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<!--This positions the Text eg name etc-->
<StackPanel Orientation ="Vertical">
<!--This changes the size of the photo on the left-->
<Image Width="445" Height="300" HorizontalAlignment="Center" Stretch="UniformToFill" >
<Image.Source>
<BitmapImage UriSource="{Binding imgSource}"/>
</Image.Source>
</Image>
<TextBlock Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
<TextBlock Text="{Binding Type}" Style="{StaticResource PhoneTextLargeStyle}" Width="1000" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have then made a seperate generic list to be held in a seperate unbound listBox so that i can select a "Type" and load up all the animals of that type.
Here is the code where i set up the unbound list
public CategorySearch()
{
InitializeComponent();
observablePets = new ObservableCollection<Shop>();
temp = new ObservableCollection<Shop>();
MyList.Add("Dog");
MyList.Add("Cat");
MyList.Add("Fish");
MyList.Add("Lizard");
lstCategory.ItemsSource = MyList;
}
and this is where i have done the SelectedIndex of the unbound listBox to add in the animals of the selected "Type"
private void lstCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lstCategory.SelectedIndex == 0)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Dog")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 1)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Cat")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 2)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Fish")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
if (lstCategory.SelectedIndex == 3)
{
foreach (Shop pet in thisApp.myshop)
{
if (pet.Type == "Lizard")
{
//lstHoldCategories.Items.Clear();
temp.Add(pet);
lstHoldCategories.ItemsSource = temp;
}
}
}
}
As you can see in this piece of code, I have commented out the piece of code that i believed would empty the listBox on the selectedIndex and reload the listBox with the new selection. Unfortunately it doesn't work and crashes the app when you select an index.
If there is a different way to empty the listBox that is bound, i would appreciate someone advising me how to do it,
Thanks in advance,
Jason
////Pics\\
This is what the page looks like before an index is selected
This is what the bound listBox will look like when you select an index
You need to clear the collection itself, rather than the object bound to the collection. A quick search showed up this... Delete all items from listobox
Just to clarify, the Items collection lives on the ListBox and that property is readonly. So you need to remove the items from the collection your ListBox is actually bound to.
you should just be able to call clear on temp prior to adding you new items. But you will need to make sure your collection source implements the INotifyCollectionChanged to see the changes reflected in the UI.

Hiding a sibling listbox when a textblock is clicked

I was wonder if any one knows how to change the visibility of a listbox within a DataTemplate when a sibling is clicked. The DataTemplate is being used on a listbox. The following is an example of the xaml I'm using:
<DataTemplate x:Key="Template1">
<StackPanel Margin="110,0,0,0">
<TextBlock Text="{Binding Name}" />
<TextBlock Name="ShowHide" Text="Hide" Tap="ShowHide_Tap" />
<ListBox Name="Listbox1" ItemsSource="{Binding SecondList}" Visibility="Visible" ItemTemplate="{StaticResource Template2}"/>
</StackPanel>
</DataTemplate>
The following is my attempt but I can't use the FindName
private void ShowHide_Click(object sender, System.Windows.Input.GestureEventArgs e)
{
var item = sender as TextBlock;
ListBox Listbox = null;
if (item != null)
{
ContentPresenter templateParent = GetFrameworkElementByName<ContentPresenter>(item);
DataTemplate dataTemplate = templateParent.ContentTemplate;
if (dataTemplate != null && templateParent != null)
{
Listbox = templateParent.FindName("Listbox1") as ListBox;
}
if (Listbox != null)
{
MessageBox.Show(String.Format("ERROR!"));
}
else
Listbox.Visibility = Visibility.Collapsed;
}
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{ break; }
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
If any one has any insights they would be much appreciated,
Thanks.
It so happens that the Blend SDK provides functionality for this -- you can even use XAML only, no code behind. Just use an EventTrigger (on the "Tap" event) along with a ChangePropertyAction. Here's how it looks:
<TextBlock Name="ShowHide" Text="Hide" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<ei:ChangePropertyAction TargetName="Listbox1"
PropertyName="Visibility" Value="Collapsed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
<ListBox Name="Listbox1" ItemsSource="{Binding SecondList}" Visibility="Visible" />
Note that this requires you to add references to the following Extensions:
System.Windows.Interactivity
Microsoft.Expression.Interactions
Reference them in XAML with the namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Welcome to StackOverflow!
Generally speaking this is not the way to use WPF, especially if you're using DataTemplates. The purpose of the view in WPF is to display the view model and fire off user events, nothing more. By changing the Data Template at run-time you're effectively storing the state of the view inside the view itself. This runs totally against the grain of how WPF was designed to be used.
To do this properly your view model (i.e. the class with the SecondList property) should have an extra property called something like ListBoxVisibility and you would bind your listbox's Visibility member to that. An even cleaner method is to use a bool and then use a converter in the view to convert it from type bool to type Visibility. Either way the view model should also have a property of type ICommand (e.g. OnButtonPressedCommand) that the button invokes when the user presses it. The handler for OnButtonPressedCommand, which should also be in the view model, then sets ListBoxVisible or whatever to the value you want which then propagates through to the list box. Doing things this way keeps good separation of concerns and means the view model can be created and its visibility-changing behavior unit tested independently without having to create the view itself.

c# - how to handle with null selectedItem?

i have an issue with selectedItem of a listbox. When I select an item of the listbox, a popup would be displayed where you click the add button to select an image (it contains a value of selectedItem) which is working fine. But after clicking the add button to select the image, then you realise the image is wrong, so you click the add button again to select another image, it started problem because selectedItem is null. How to handle it? How to stay the value of selectedItem? Your given code much appreciated.
if (lstDinner.SelectedItem != null)
{
output = _imageInserter.InsertImage(imageName, lstDinner.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
strDinner.DinnersDetails = lstDinner.SelectedItem.ToString()
}
else
{
// strDinner.DinnersDetails = null that cause a problem.
output = _imageInserter.InsertImage(imageName, strDinner.DinnersDetails);
PopupDinnerImage.IsOpen = true;
}
UPDATE HERE:
WPF:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown" MouseDoubleClick="lstDinner_MouseDoubleClick" >
events in C#:
private void MouseDownHandler(object sender, MouseButtonEventArgs e)
{
var parent = (ListBox)sender;
_dragSource = parent;
var data = GetObjectDataFromPoint(parent, e.GetPosition(parent));
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 1)
{
if (data != null)
DragDrop.DoDragDrop(parent, data, DragDropEffects.Move);
}
}
private void lstDinner_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
RemoveItemsFromDatabase();
}
}
private void lstDinner_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_dinnerImage = new DinnerImageExtractor();
BitmapImage getImage = new BitmapImage();
if (lstDinner.SelectedItem != null)
{
getImage = _dinnerImage.GetDinnerImages(lstDinner.SelectedItem.ToString());
if (getImage != null)
{
DinnerImagePopup.Source = getImage;
}
else
{
DinnerImagePopup.Source = new BitmapImage(new Uri("/DinnerApplicationWPF;component/Menu/Images/noImage-icon-pink.png", UriKind.Relative));
}
PopupDinnerImage.IsOpen = true;
// PopupInstrcution.IsOpen = false;
}
}
I would suggest something like this
if ( lstDinner.SelectedItem == null)
{
output = _imageInserter.InsertImage(imageName, lstToys.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
lstDinner.Databind();
}
Note: This may not work as I dont have your actual code. I have added DataBind() in the if statement, if the selected item was null. It should refresh the list.
Best thing is to use two different Listbox item templates for selected and unselected items. So without displaying popup, you can add button into the selected item template.
Are you disabling the ListBox while you select the image?
If so I believe by simply disabling the ListBox the SelectedItem will be set to null.
EDIT:
I imagine you want your event handlers (like the mouse double click) to happen when an item in your list is double clicked, not when the ListBox is double clicked. You need to change your XAML to this:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<EventSetter Event="MouseDoubleClick" Handler="lstDinner_MouseDoubleClick" />
</Style>
</ListBox.Resources>
</ListBox>
My selected item does not come up null when I run this code.

Bound ItemsControl not updating before displaying

I have a UserControl that is comprised of a few bound ItemsControl's and strings, and based on the button that is pressed, different data is displayed. Here is an example of one of the Button's click events:
private void LeftPreviousScoresButton_Click(object sender, RoutedEventArgs e)
{
if (m_previousScoresWindow.Visibility == Visibility.Visible)
{
m_previousScoresWindow.Hide();
}
else
{
WindowTitle = "Left Side";
PreviousScoresA = m_previousLeftWristErosionScoresReaderA;
PreviousScoresB = m_previousLeftWristErosionScoresReaderB;
m_previousScoresWindow.Show();
}
}
There are several of these click event listeners which assigns WindowTitle, PreviousScoresA, and PreviousScoresB with the associated data. The UserControl then binds to them like this:
<ItemsControl Height="Auto" Width="Auto"
ItemsSource="{Binding ElementName=ParentForm, Path=PreviousScoresA}"
Grid.Row="1" />
<ItemsControl Height="Auto" Width="Auto"
ItemsSource="{Binding ElementName=ParentForm, Path=PreviousScoresB}"
Grid.Row="2" />
<TextBlock FontSize="16" FontWeight="Bold" Height="25"
Margin="5" HorizontalAlignment="Center" Foreground="Black"
Text="{Binding ElementName=ParentForm, Path=PreviousScoresWindowTitle}" />
However, when opening the window, the old data displays for a second before it is updated with the current data. I've even tried adding these calls when calling Hide() on the Window but it didn't seem to help:
WindowTitle = String.Empty;
PreviousScoresA = new ObservableCollection<PreviousScoreData>();
PreviousScoresB = new ObservableCollection<PreviousScoreData>();
Is there any way to ensure that Show() is not called until after the bound data has been updated? Thanks.
As it appears you are using an ObservableCollection, the collection should never be re-initialized. Rather, it should just be cleared and then add the new values; this is what helps keep the collection synchronized when using an ObservableCollection.
This is a bit of a shot in the dark based on your code sample; if you clear the collection when hiding and then refill them with the new values, then you should get the desired effect:
private void LeftPreviousScoresButton_Click(object sender, RoutedEventArgs e)
{
if (m_previousScoresWindow.Visibility == Visibility.Visible)
{
m_previousScoresWindow.Hide();
WindowTitle = string.Empty;
PreviousScoresA.Clear();
PreviousScoresB.Clear();
}
else
{
WindowTitle = "Left Side";
// do not re-initialize the collection; clear and add new values
// PreviousScoresA = m_previousLeftWristErosionScoresReaderA;
// PreviousScoresB = m_previousLeftWristErosionScoresReaderB;
ReFillScores(PreviousScoresA, m_previousLeftWristErosionScoresReaderA);
ReFillScores(PreviousScoresB, m_previousLeftWristErosionScoresReaderB);
m_previousScoresWindow.Show();
}
}
private void ReFillScores (ObservableCollection<PreviousScoreData> collection, IEnumerable<PreviousScoreData> values)
{
collection.Clear();
foreach(PreviousScoreData d in values)
{
collection.Add(d);
}
}

Categories