I am working on my application update and I want to use a new searchbox and I want to show my results like Windows Store .
how can I do this ?
You can use an AutoSuggestBox which is bound to a changing ObservableCollection everytime the Text inside the AutoSuggestBox is changed.
For example, this is your Model:
public class App
{
public ind Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public Image Picture { get; set; }
}
You can implement a method updating an ObservableCollection with a parameter (in this case the search expression) in your ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
SuggestedApps = new ObservableCollection<App>();
SuggestedApps.CollectionChanged += SuggestedApps_CollectionChanged;
}
private void SuggestedApps_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("SuggestedApps");
}
private ObservableCollection<App> suggestedApps;
public ObservableCollection<App> SuggestedApps
{
get
{
return suggestedApps;
}
set
{
suggestedApps = value;
OnPropertyChanged("SuggestedApps");
}
}
public void SuggestForSearch(string searchExpression)
{
SuggestedApps.Clear();
//Assumgin EF as DataSource
//You can use another Search algorithm here instead of String.Contains
foreach(var item in yourDataSource.Apps.Where(x => x.Name.Contains(searchExpression.Trim())))
{
SuggestedApps.Add(item);
}
}
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
In your Xaml code you can use this to bind an AutoSuggestBox to it and define a Template:
<AutoSuggestBox x:Name="AutoSuggestBoxApps" ItemsSource="{Binding SuggestedApps}" TextChanged="AutoSuggestBoxApps_TextChanged">
<AutoSuggestBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Picture}"/>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Category}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
In the implemetation of the TextChanged-Event you just call the SuggestForSearch Method from your ViewModel:
private void AutoSuggestBoxApps_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
(this.DataContext as ViewModel).SuggestForSearch((sender as AutoSuggestBox).Text);
}
There is a control for UWP named AutoSuggestBox that you should read up on:
https://msdn.microsoft.com/nb-no/library/windows/apps/xaml/windows.ui.xaml.controls.autosuggestbox.aspx
This should give you the tools you need to give the wanted functionality
Related
I'm pretty new to programming with WPF and C# and I have a question regarding the possibility to automatically check all the CheckBoxes in a Listbox. I'm developing a plugin for Autodesk Revit and, after having listed all the names of the rooms in a list box, I want to check them all using the button "Check All"
I've read the thread at this page but still, I'm not able to make it work. May someone help me with my code?
Here is what I've done:
XAML:
<ListBox x:Name='roomlist'
SelectionMode='Multiple'>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked='{Binding IsChecked}'
Content="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.InputBindings>
<KeyBinding Command="ApplicationCommands.SelectAll"
Modifiers="Ctrl"
Key="A" />
</ListBox.InputBindings>
<ListBox.CommandBindings>
<CommandBinding Command="ApplicationCommands.SelectAll" />
</ListBox.CommandBindings>
</ListBox>
C#
public partial class RoomsDistance_Form : Window
{
UIDocument _uidoc;
Document _doc;
public RoomsDistance_Form(Document doc, UIDocument uidoc)
{
InitializeComponent();
FilteredElementCollector collector = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.OfCategory(BuiltInCategory.OST_Rooms);
List<String> myRooms = new List<String>();
foreach (var c in collector)
{
myRooms.Add(c.Name);
}
myRooms.Sort();
roomlist.ItemsSource = myRooms;
}
private void checkAllBtn_Click(object sender, RoutedEventArgs e)
{
foreach (CheckBox item in roomlist.Items.OfType<CheckBox>())
{
item.IsChecked = true;
}
}
public class Authority : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Thank you very much for your help!
In the thread you are linking to, they are setting the "IsChecked" on the data object (Authority), not the CheckBox control itself.
foreach (var a in authorityList)
{
a.IsChecked = true;
}
You have a binding to IsChecked that will update the Checkbox control when NotifyPropertyChanged() is called.
After having lost my mind in the effort i solved my problem by avoiding the Listbox.. I simply added single CheckBoxes in the StackPanel.
XAML:
<ScrollViewer Margin='10,45,10,100'
BorderThickness='1'>
<StackPanel x:Name='stack'
Grid.Column='0'></StackPanel>
</ScrollViewer>
C#:
foreach (var x in myRooms)
{
CheckBox chk = new CheckBox();
chk.Content = x;
stack.Children.Add(chk);
}
Not what i was looking for but now it works and that's the point.
Thank you for your help!
I usually use CheckBoxList in the following way:
In xaml:
<ListBox ItemsSource="{Binding ListBoxItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> //+some dimensional properties
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In xaml.cs:
public partial class MyWindow : Window
{
public ViewModel ViewModel {get; set; }
public MyWindow(ViewModel viewModel)
{
//keep all the mess in ViewModel, this way your xaml.cs will not end up with 1k lines
ViewModel = viewModel;
DataContext = ViewModel;
InitializeComponent();
}
void BtnClick_SelectAll(object sender, RoutedEventArgs e)
{
ViewModel.CheckAll();
}
}
ViewModel preparation:
public class ViewModel
{
public List<ListBoxItem> ListBoxItems { get; set; }
//InitializeViewModel()...
//UpdateViewModel()...
//other things....
public void CheckAll()
{
foreach (var item in ListBoxItems)
{
item.IsSelected = true;
}
}
public class ListBoxItem : INotifyPropertyChanged
{
public string Name { get; set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I am having List Box which contain the buttons. We can add any no of buttons. I have added the buttons into the list called "AddedButtonList" through c# and bind that list as follow:
<Grid Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1">
<Grid.Resources>
<DataTemplate DataType="{x:Type viewModel:AddedAction}">
<Button Content="{Binding Title}"
Height="40"
Width="100"
Command="{Binding Command}">
</Button>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding actionsRecordVmObj.AddedActionsList}" Width="Auto">
</ListBox>
</Grid>
we can add any no of buttons by using above code in xaml because i have bind all the properties from code behind.
the code behind is:
public abstract class AddedAction
{
public bool IsDisable { get; set; }
public string Title { get; set; }
public string ButtonIndex { get; set; }
public abstract ICommand Command { get; }
}
public class AddedSourceFileActionVm : AddedAction
{
public ICommand _command;
//constructor
public AddedSourceFileActionVm()
{
Title = "Source File";
_command = new RelayCommand(p => AddedSourceFileActionCommandExecuted(null), p => CanAddedSourceFileActionCommandExecute());
}
All buttons are bind with command
(buttons may be repeat in the list).
I want to get the index of the button(Item from list) which get pressed.
I read many answers some of were saying use AlternationCount but when I want the index in code behind I am not able to do that because I use Command for binding and they showed for click event.
I cant use
private void lstButton_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
int index = _myListBoxName.Items.IndexOf(button.DataContext);
}
because I am using MVVM and I bind all buttons with command.
so please suggest some solution for this.
or in short how to get the index of button which is pressed from list?
Thanks in advance....
Here's how you can easily get the index:
1. Define an abstract event on your AddedAction.
2. Subscribe to the event when creating an AddedAction instance
3. Raise the event when AddedAction.Command is executed
4. Get the index on the event handler
For example:
public abstract class AddedAction
{
//define the event
public abstract event EventHandler CommandExecuted;
public abstract ICommand Command { get; }
//...
}
public class AddedSourceFileActionVm : AddedAction
{
public override event EventHandler CommandExecuted;
private void AddedSourceFileActionCommandExecuted(object obj)
{
//invoke the event
CommandExecuted?.Invoke(this, null);
//...
}
//...
}
public class ActionsRecordVm
{
public List<AddedAction> AddedActionsList { get; } = new List<AddedAction>();
public void AddNewAddedAction()
{
var addedAction = new AddedSourceFileActionVm();
//Subscribe to the event
addedAction.CommandExecuted += AddedAction_CommandExecuted;
AddedActionsList.Add(addedAction);
}
private void AddedAction_CommandExecuted(object sender, EventArgs e)
{
//get the index
int index = AddedActionsList.IndexOf((AddedAction)sender);
//...
}
//...
}
I want to achieve one-way binding from an ObservableCollection of "struct-like" items to a TextBox that has a TextChanged event. The idea is that as the Comments field of Item accumulates in the TextBox, the TextBox scroll down automatically so that the last line is always in view. The collection is bound to a ListView but I want it bound read-only to the TextBox. I would prefer not to add another method in ResultViewModel but do it in XAML. How would I go about in doing this? TIA
// ViewModel
public class Item
{
public string First { get; set; }
public string Last { get; set; }
public string Comments { get; set; }
}
public class ResultViewModel
{
private ObservableCollection<Item> items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items { get { return items; } }
// member functions
}
public ResultViewModel ViewModel { get; set; }
// What I have
<ListView x:Name="myListView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{x:Bind ViewModel.Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<StackPanel>
<TextBox Text="{x:Bind First}"/>
<TextBlock Text="{x:Bind Last}"/>
<TextBlock Text="{x:Bind Comments}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
// What I want
<TextBox Text="{x:Bind Comments}"/>
I'm afraid you can't do it with XAML alone.
You can create a behavior which will listen to events and add lines to the textbox when the collection is modified.
Dirty example, you will need to include Microsoft.Xaml.Behaviors.Uwp.Managed package:
public class CommentsBehavior : Behavior
{
public ObservableCollection<string> Comments ... // you will need to make it a dependency property
protected virtual void OnAttached()
{
Comments.CollectionChanged += OnCollectionChanged;
}
protected virtual void OnDetaching()
{
Comments.CollectionChanged -= OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach(string newItem in e.NewItems)
{
((TextBox)AssociatedObject).Text = ((TextBox)AssociatedObject).Text + '\n' + newItem;
}
}
}
}
And for scrolling - UWP C# Scroll to the bottom of TextBox
But why do you want to use textbox for this? Using list makes more sense.
I am working on a combobox for more than 2 days but did not find the solution.
In one of my question a user named JnJnBoo tried to answer my question and I got some knowledge and code from there.
I am trying to display some data in a combobox in multiple columns using MVVM pattern.
I am using entity framework and SQL Server database.
Here is the code :
namespace ERP_Lite_Trial.ViewModels
{
public class GroupsViewModel : INotifyPropertyChanged
{
public GroupsViewModel()
{
using (DBEntities db = new DBEntities())
{
GroupsAndCorrespondingEffects = (from g in db.Groups
select new GroupAndCorrespondingEffect
{
GroupName = g.Name,
CorrespondingEffect = g.Type_Effect.Name
}
).ToList();
EffectName = (from e in db.Type_Effect
select e.Name).ToList();
}
}
private List<GroupAndCorrespondingEffect> _groupsAndCorrespondingEffects;
public List<GroupAndCorrespondingEffect> GroupsAndCorrespondingEffects
{
get
{
return _groupsAndCorrespondingEffects;
}
set
{
_groupsAndCorrespondingEffects = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
}
}
private string _selectedGroup;
public string SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}
private List<string> _effectName;
public List<string> EffectName
{
get
{
return _effectName;
}
set
{
_effectName = value;
OnPropertyChanged("EffectName");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
}
And the XAML :
<ComboBox x:Name="cbUnder" ItemsSource="{Binding Path=GroupsAndCorrespondingEffects}"
IsEditable="True" SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
TextSearch.TextPath="GroupName" Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=GroupName}"/>
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I tried many things but always unsuccessful.
My Combobox is not displaying any data in its items but when I select any Item in the combobox then I get some data in the selectedGroup property. The data is namespace.classname
So I think I need to override the Tostring Method of GroupAndCorrespondingEffect class. But It has got two properties. In which format the databinding in XAML expects the data that is not known to me. So, how to override the tostring method? Or might be I am making some sort of mistake in my code?
Your GroupAndCorrespondingEffect should look like the following
public class GroupAndCorrespondingEffect
{
public string GroupName;
{
get;
set;
}
public string CorrespondingEffect;
{
get;
set;
}
}
And in you XAML
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
The property name is wrong it contains additional s
so it should be
<TextBlock Text="{Binding Path=CorrespondingEffect}"/>
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
Make the public variables GroupName and CorrespondingEffect as properties
And in your View model change the type of the property SelectedGroup like below
private GroupAndCorrespondingEffect _selectedGroup;
public GroupAndCorrespondingEffect SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}
I wanted to add a list of text to ListBox as soon as the user press the Button..Each ListItem contains TextBlock to which am binding the data..
But the TextBlock is not showing the text! Though I could see the Background color of each Item being inserted!
<StackPanel>
<Button Content="CLICK" Click="Button_Click"></Button>
<ListBox x:Name="dataList" Foreground="Red" Background="Blue">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Feed}" FontSize="28"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
My code behind looks like
public partial class MainPage : UserControl
{
ObservableCollection<Data> data;
public MainPage()
{
InitializeComponent();
data = new ObservableCollection<Data>();
dataList.ItemsSource = data;
}
class Data :INotifyPropertyChanged
{
public Data(String s)
{
Feed = s;
}
private string _feed;
public String Feed
{
get { return _feed; }
set { _feed = value; NotifyPropertyChanged("Feed"); }
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
data.Add(new Data("News1"));
data.Add(new Data("News2"));
data.Add(new Data("News2"));
}
}
Thanks..
Your class Data needs to be public else it would have private access specifier by default..
So it should be
public class Data.....
Everything else seems to be ok..