AutoComplete list update? - silverlight

Evening all,
I have a basic autocompletebox using silverlight5.
The aim is to be able to search through a list of people and remove certain people from this list via checkboxes above.
On the checkbox event the list is modified but this is not reflected in the autocompletebox.
.xaml:
<StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="Transparent">
<sdk:Label Content="Filter By:" FontSize="12" Name="label1" Margin="10,10,10,5" />
<CheckBox Content="Students" Height="16" Name="checkBox1" Margin="10,5,10,0" Checked="checkBox1_Checked" Unchecked="checkBox1_Checked"/>
<CheckBox Content="Staff" Height="16" Name="checkBox2" Margin="10,5,10,0" Checked="checkBox2_Checked" Unchecked="checkBox2_Checked"/>
<CheckBox Content="Guest" Height="16" Name="checkBox3" Margin="10,5,10,10" Checked="checkBox3_Checked" Unchecked="checkBox3_Checked"/>
<sdk:AutoCompleteBox x:Name="peoplelist"/>
</StackPanel>
Code behind:
public CustomerFilterControl()
{
InitializeComponent();
//_viewModel.Initialize(); initial loading of context data, populate dropdowns etc
people.Add("Student 1");
//.....................add more
peoplelist.Itemssource = people;
}
Checkbox methods:
private void checklist()
{
if (checkBox1.IsChecked.Value)
{
people.Clear();
people.Add("Guest 1");
//.................... add more
peoplelist.DataContext = people;
}
Lots of searching has pointed me to many work around for earlier versions of sliverlight but I'm practically going around in circles at this point.
Could anyone point me in the right direction to getting this functioning?

Replace List<string> by ObservableCollection<string>
This generic raises the CollectionChangedEvent so bound controls know they need to update.

Related

Real Time filtering of the ListBox

Short background on the problem:
I am working on the UI part of an WPF application. But since I have finished my part pretty fast - decided to try implement ListBox filtering function.
What I have at this point:
<TextBox Style="{StaticResource WaterMarkMessageTextBoxStyle}"
x:Name="usuariosDisponiblesSearch"
Grid.Column="1" Grid.Row="1"/>
<ListBox Style="{StaticResource ListBoxTable}"
ItemContainerStyle="{StaticResource AlternatingListViewItemStyle}"
AlternationCount="2"
Grid.Column="1" Grid.Row="2"
x:Name="lbPossibleContracts"
SelectionChanged="lbPossibleContracts_SelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Nombre}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
TextBox contains a searching string.
ListBox - reflects data, picked from the DB by field Nombre.
On the end point - I want to have working filter, which shows in the ListBox only the items, which match with the string in the TextBox. Real time.
Of course, I understand that it is impossible to do, without modifying the back code.
The problem is that so far my approaches for implementation of filtering functionality were unsuccessful, mainly because I didn't understood what exactly I am implementing into the back code.
Any help on this will be appreciated. Works in any direction - if you want produce the whole part of code ( :) ) - you are welcomed, if you have some good, understandable reference to an equal, solved problem -it is great as well.
UPDATE
Ok, I have made some pretty huge progress in the subject.
I was following this example: http://www.wpf-tutorial.com/listview-control/listview-filtering/
Here are the codes:
<TextBox Style="{StaticResource WaterMarkMessageTextBoxStyle}"
x:Name="usuariosDisponiblesSearch"
Grid.Column="1" Grid.Row="1"
TextChanged="usuariosDisponiblesSearch_TextChanged" />
<ListBox Style="{StaticResource ListBoxTable}"
ItemContainerStyle="{StaticResource AlternatingListViewItemStyle}"
AlternationCount="2"
Margin="0,20,0,0"
x:Name="lbPossibleContractsFilter"
SelectionChanged="lbPossibleContracts_SelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Nombre}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And here is the backcode:
public partial class ProjectAssignUsuariosView : UserControl
{
private ProjectAssignUsuariosViewModel _viewModel;
public ProjectAssignUsuariosView(Proyecto proyecto)
{
InitializeComponent();
_viewModel = new ProjectAssignUsuariosViewModel(proyecto);
//TempFilterPart
lbPossibleContractsFilter.ItemsSource = _viewModel.UsuariosLibres;
List<Usuario> items = new List<Usuario>();
filteredUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lbPossibleContractsFilter.ItemsSource);
view.Filter = UserFilter;
//Filtering part ends
}
//Filtering
private bool UserFilter(object item)
{
if (String.IsNullOrEmpty(usuariosDisponiblesSearch.Text))
return true;
else
return ((item as Usuario).Nombre.IndexOf(usuariosDisponiblesSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0);
}
private void usuariosDisponiblesSearch_TextChanged(object sender, TextChangedEventArgs e)
{
CollectionViewSource.GetDefaultView(lbPossibleContractsFilter.ItemsSource).Refresh();
}
//Filtering ends
No errors while compiling. But when running and change the TextBox - throws Unexpected application error. And the debuger says "NullReferenceException was unhandled by user code. Additional information: Object reference not set to an instance of an object."
Ok, I have made a stable working prototype for described purpose.
I was following this example: http://www.wpf-tutorial.com/listview-control/listview-filtering/
The only difference is that my ItemsSource had been placed outside.
Here are the working codes (simplified).
XAML:
<ListBox Style="{StaticResource ListBoxTable}"
ItemContainerStyle="{StaticResource AlternatingListViewItemStyle}"
AlternationCount="2"
Grid.Column="1" Grid.Row="2"
x:Name="lbPossibleContracts"
SelectionChanged="lbPossibleContracts_SelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Nombre}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Important parts here are the ListBox x:Name Property, which gives access to the ItemsSource. And ListBox SelectionChanged Propert, which calls for refresh of the results.
XAML.CS:
namespace Todiste.Views.Proyectos
{
public partial class ProjectAssignUsuariosView : UserControl
{
private ProjectAssignUsuariosViewModel _viewModel;
public ProjectAssignUsuariosView(Proyecto proyecto)
{
InitializeComponent();
_viewModel = new ProjectAssignUsuariosViewModel(proyecto);
lbCurrentContracts.ItemsSource = _viewModel.UsuariosProyecto;
lbPossibleContracts.ItemsSource = _viewModel.UsuariosLibres;
//Filter Starts
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lbPossibleContracts.ItemsSource);
view.Filter = UserFilter;
//Filter Ends
}
//Filter Starts
private bool UserFilter(object item)
{
if (string.IsNullOrEmpty(filter.Text))
return true;
else
return ((item as Usuario).Nombre.IndexOf(filter.Text, StringComparison.OrdinalIgnoreCase) >= 0);
}
private void filter_TextChanged(object sender, TextChangedEventArgs e)
{
CollectionViewSource.GetDefaultView(lbPossibleContracts.ItemsSource).Refresh();
}
// Filter Ends
Here are three main points, worth attention:
lbPossibleContracts.ItemsSource = _viewModel.UsuariosLibres; - assigning outside datasource to the list
Collection view = - defining, the view
UserFilter and filter_TextChanged - are actually the functions, which are performing filtering and list refreshing actions
Not the best style, but a working one.

Binding Single Item of a Collection

I'm just learning the basic concepts of WPF and XAML coming from a C++ background, so some of it is a bit alien to me. I am using Expression Blend to help me get to grips with XAML.
I am making a basic app that displays records in a simple XML data source:
<photos>
<photo>
<image>Assets\Item01.png</image>
<description>Strawberry</description>
</photo>
<photo>
<image>Assets\Item02.png</image>
<description>Orange</description>
</photo>
<photo>
<image>Assets\Item03.png</image>
<description>Pineapple</description>
</photo>
...
</photos>
I have bound this data 'photoDataSource' to a grid and stuck some textboxes and image fields that display the first record. In XAML:
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource photoDataSource}}" Margin="0,0,0,1" Background="#FF1D1D1D">
<Image Height="104" Width="104" Source="{Binding XPath=/photos/photo/image}" Margin="8,62,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox Height="23" Margin="8,8,6,0" TextWrapping="Wrap" Text="{Binding XPath=/photos/photo/description}" VerticalAlignment="Top"/>
<TextBox Height="23" Margin="8,35,6,0" TextWrapping="Wrap" Text="{Binding XPath=/photos/photo/image}" VerticalAlignment="Top"/>
<Button Content="Next Product" Margin="213,97,297,0" Height="44" VerticalAlignment="Top"/>
</Grid>
This displays two textboxes containing "Strawberry" and "Assets\Item01.png" respectively, along with the image and a Button Containing the text "Next Product". As you can see I have bound the collection "photoDataSource" to the parent Grid. When run it displays the first item in the collection.
How can I trigger the button to display the next item in the collection (and loop) at runtime?
I am not intending to do this with any code-behind as I am not changing any of the data itself, just which item is displayed. But perhaps I am going about this in the wrong way?
Ideally after this example I will want to remove the button completely and change records automatically after a storyboard animation has completed (using the trigger 'StoryboardCompletedTrigger').
Quite right not wanting to use code behind. However I would recommend implementing a ViewModel against your Window to get what you want achieved.
In your view model you should have an ObservableCollection of your Photo object and another property to specify a single Photo being called SelectedPhoto as shown below:
public ObservableCollection<Photo> MyPhotos {
get { return _photos; }
set { _photos = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Photos"));
}
}
public Photo SelectedPhoto {
get { return _photo; }
set { _photo = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
Then use XmlSerialization to load your Xml into the ObservableCollection. Then create your buttons to move next and previous to bind to an ICommand (also in your ViewModel) to cycle up or down the MyPhotos collection setting SelectedPhoto each time.
Then you can bind and Image in your Xaml as follows.
<Image Source="{Binding Source={StaticResource myViewModel}, Path=SelectedPhoto.Image}"/>
I hope this makes some sense for you and has been of some help.

Binding on dynamically-added elements

TPTB have decided that our app must run in a single window, popping up new windows in modal mode is not allowed.
And naturally, we have a UI design that involves popping up modal dialogs all over the place.
So I added a top-level Grid to the Window. In that Grid I defined no rows or columns, so everything draws in Row 0/Column 0.
The first element in the Grid was another Grid that contained everything that was normally displayed in the Window. The second was a full-sized Border with a gray, semi-transparent Background. The rest were Borders with wide Margins and white Backgrounds, containing the various UserControls that needed to be displayed as popups. All but the first had Visibility="Collapsed".
And then, when I needed to show a popup, I'd set Visibility="Visible" on the gray background and on the appropriate UserControl. The result was a nice shadowbox effect that worked fine.
Until somebody decided that the popups needed to be able to display popups. In a non-predictable order.
The limitation of the method I had implemented, using Visibility="Collapsed" elements in a Grid was that their order was fixed. UserControlB would always be displayed on top of UserControlA, even if it was UserControlB that asked to have UserControlA displayed. And that's not acceptable.
So my next attempt was to define the various UserControls in Window.Resources, and to add them to the Grid in code:
this.masterGrid.Children.Add(this.Resources["userControlA"] as UserControlA);
And that almost works. But the bindings are all messed up.
As an example, one of the controls is supposed to bind a Property to the CurrentItem of a collection in a member object of the Window's viewmodel. When I had the control defined as an invisible item in the Grid, it worked fine. But when I defined it as a Resource, the Property was null - it was never bound.
So I tried binding it in code, after I added it to the grid:
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
And that compiles and runs just fine, but I'm not binding to the right object.
The first time I display the UserControl, I see the right object bound to it. But when I close it, and move the CurrentItem in the collection to a different object, and display the UserControl again, I still see the first object bound. If I close it again, and open it a third time, then I will see the right object bound to the control.
I've checked in code, and the CurrentItem that I'm binding to is right, every time, but it only seems to take every other time.
So I tried explicitly clearing the binding, first:
BindingOperations.ClearBinding(userControlA, UserControlA.myProperty);
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
But that doesn't seem to have made any difference.
In all, it feels like I'm running down a rabbit hole, chasing deeper and deeper into complexity, to solve what should be a fairly simple problem.
Does anyone have any suggestions as to:
How to get binding to work on dynamically-added elements, or
How to get arbitrarily-ordered popups to display, as shadowboxes, without using dynamically-ordered elements?
Thanks in advance.
While it seems really odd for me that you can't create new Windows, I would definitely recommend not to complicate it too much by doing unnecesary things such as storing your views in the MainWindow's resources.
It would be better if you just added new instances of these elements into an ObservableCollection:
XAML:
<Window x:Class="WpfApplication4.Window8"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="Window8" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<StackPanel Background="Green">
<TextBlock Text="This is ViewModel1!!"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<StackPanel Background="Blue" HorizontalAlignment="Center">
<TextBlock Text="This is ViewModel2!!"/>
<TextBlock Text="{Binding Text2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel3}">
<StackPanel Background="Red" VerticalAlignment="Center">
<TextBlock Text="This is ViewModel3!!"/>
<TextBlock Text="{Binding Text3}"/>
<TextBox Text="{Binding Text3}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<Button Width="100" Content="Add" Click="Add_Click" DockPanel.Dock="Top"/>
<Button Width="100" Content="Remove" Click="Remove_Click" DockPanel.Dock="Top"/>
<ListBox ItemsSource="{Binding ActiveWidgets}" SelectedItem="{Binding SelectedWidget}">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ListBox.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DockPanel>
</Window>
Code Behind:
using System.Linq;
using System.Windows;
using System.Collections.ObjectModel;
using System;
namespace WpfApplication4
{
public partial class Window8 : Window
{
private WidgetsViewModel Widgets { get; set; }
public Window8()
{
InitializeComponent();
DataContext = Widgets = new WidgetsViewModel();
}
private Random rnd = new Random();
private int lastrandom;
private void Add_Click(object sender, RoutedEventArgs e)
{
var random = rnd.Next(1, 4);
while (random == lastrandom)
{
random = rnd.Next(1, 4);
}
lastrandom = random;
switch (random)
{
case 1:
Widgets.ActiveWidgets.Add(new ViewModel1() {Text = "This is a Text"});
break;
case 2:
Widgets.ActiveWidgets.Add(new ViewModel2() { Text2 = "This is another Text" });
break;
case 3:
Widgets.ActiveWidgets.Add(new ViewModel3() { Text3 = "This is yet another Text" });
break;
}
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
private void Remove_Click(object sender, RoutedEventArgs e)
{
Widgets.ActiveWidgets.Remove(Widgets.SelectedWidget);
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
}
public class WidgetsViewModel: ViewModelBase
{
public ObservableCollection<ViewModelBase> ActiveWidgets { get; set; }
private ViewModelBase _selectedWidget;
public ViewModelBase SelectedWidget
{
get { return _selectedWidget; }
set
{
_selectedWidget = value;
NotifyPropertyChange(() => SelectedWidget);
}
}
public WidgetsViewModel()
{
ActiveWidgets = new ObservableCollection<ViewModelBase>();
}
}
public class ViewModel1: ViewModelBase
{
public string Text { get; set; }
}
public class ViewModel2: ViewModelBase
{
public string Text2 { get; set; }
}
public class ViewModel3: ViewModelBase
{
public string Text3 { get; set; }
}
}
Just copy and paste my code in a File - New - WPF Application and see the results for yourself.
Since the Grid always places the last UI Element added to it topmost, you will see that Adding items to the observablecollection makes these "different widgets" always appear on top of each other, with the topmost being the last one added.
The bottom line is, when WidgetA requests to open WidgetB, just create a new WidgetBViewModel and add it to the ActiveWidgets collection. Then, when WidgetB is no longer needed, just remove it.
Then, it's just a matter of putting your UserControls inside a proper DataTemplate for each ViewModel. I strongly suggest you keep a separate ViewModel for each of your Widgets, and if you need to share data between them, just share data between the ViewModels.
Don't attempt to do things like ListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" unless you have a good reason to.
This way you no longer have to deal with Panel.ZIndex stuff. Maybe you can create a couple of attached properties to deal with things like focus and whatnot, but this approach is dead simple, and by far more performant than the Visibility and the Resources approaches.

WPF Expander, get the Header name out of a child element

I have a WPF Expander, such as this:
<Expander Canvas.Left="251" Canvas.Top="425" Header="expander1" Height="100" Name="expander1">
<Grid>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Option 1" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 2" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 3" Checked="chk_DoThis" />
</StackPanel>
</Grid>
</Expander>
When a checkbox is clicked, I fire off a 'Checked' event.
Is there some way to pull out a string that contains the 'Header' of the Expander? In this example, I want to pull out 'expander1' and assign that to a string.
I tried a few ways of doing this and couldn't get it to work. I have done this same concept using TreeViewItems and using a Header.Parent.ToString() to get what I wanted. No luck here. This is what I'm referring to:
string child = ((TreeViewItem)((TreeViewItem)((TreeView)sender).SelectedItem)).Header.ToString();
Does anyone know of a way I could do this for my Expander example. Googling and searching this site has yielded no return. It's probably something easy and I'm just overlooking it.
Thanks to anyone that has some ideas.
You can easily get the Expander from the CheckBox. Just iterate to the VisualTree and get the Top most parent's Expander. I just simply did only one parent in the below example.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Expander expander = (Expander)checkBox.Parent;
if (expander != null)
{
string str = expander.Header.ToString();
Debug.WriteLine(str);
}
}
I hope it will help you.

How to rotate Items in a WPF Listbox style?

I want to make a WPF ListBox photo album for one my college projects.
I need to design a DataTemplate/ListBox style so it will look like a stack jumbled of photos, i.e., the top one being the item in focus/selected (see diagram below).
Image here
With reference to the drawing,
item 1) is not shown
item 2) is at the back of stack
item 3) in the middle of 2 and 4
item 4) is in focus
item 5) is not shown
I am having the most trouble getting the items to rotate and overlap and the most difficult task is getting the item in focus to be shown on top.
I'm using Visual Basic because I haven't yet mastered C# so it would be useful if examples could be in VB or use mainly WPF.
To get the items to rotate, you should look at using Transforms. The one that is most relevant in this case is the Rotate Transform, also to give it that random scattered apperance, we can use an ObjectDataProvider and generate our angles somewhere else.
I don't know VB, but the only C# involved in this is pretty simple, and should be easily transferable.
Lets just use something simple like Colors, which can easily be switched over to image resource paths. Here we've got an ObservableCollection of our Colors, and also a separate class that we will use to generate angles, all it's going to do is return a number between 0 and 360, which we will use to rotate each item.
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Colors = new ObservableCollection<string>();
Colors.Add("Red");
Colors.Add("Blue");
Colors.Add("Green");
Colors.Add("Yellow");
this.DataContext = this;
}
public ObservableCollection<string> Colors
{
get;
set;
}
}
public class AngleService
{
private static Random rand = new Random();
public int GetAngle()
{
return rand.Next(0, 90);
}
}
In the XAML, we can now create a resource that can be used to generate the angles.
<Window.Resources>
<local:AngleService x:Key="Local_AngleService" />
<ObjectDataProvider x:Key="Local_AngleProvider"
x:Shared="False"
ObjectInstance="{StaticResource Local_AngleService}"
MethodName="GetAngle" />
</Window.Resources>
Now, we just need to create something to display our items. We can put them in a ListBox and add a data template to set the background for each color item, as well as apply the RotateTransform.
<ListBox ItemsSource="{Binding Colors}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="uiItemBorder"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="3"
Background="{Binding}"
Width="50"
Height="50">
<TextBlock Text="{Binding}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Border.RenderTransform>
<RotateTransform Angle="{Binding Source={StaticResource Local_AngleProvider}}" />
</Border.RenderTransform>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The UI still needs a bit of work from there, but that should help out with the rotation of the items.

Resources