I have two ToggleButtons; I'm trying to make them behave like a pair of radio buttons by binding them to booleans, but it's not working. Here's what I have so far:
<ToggleButton Name="YesButton" Margin="5,0" Width="100" IsChecked="{Binding YesBool, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Yes!</ToggleButton>
<ToggleButton Name="NoButton" Margin="5,0" Width="100" IsChecked="{Binding NoBool, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">No!</ToggleButton>
And
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
}
public class Thingy : INotifyPropertyChanged
{
private bool _yesno;
public bool YesBool
{
get { return _yesno; }
set { _yesno = value; NotifyPropertyChanged("YesBool"); }
}
public bool NoBool
{
get { return !_yesno; }
set { _yesno = !value; NotifyPropertyChanged("NoBool"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As far as I can tell, everyone else who had this problem misspelled their binding or didn't use NotifyPropertyChanged, but (as far as I can tell) I'm doing both of those things. What am I doing wrong?
Set your DataContext in your xaml to your Thingy class instead of "this" Window.
You're question doesn't specify if the booleans are needed or are only there to help you get the wanted behaviour.
So in case you don't need them, you could also go for a function, which unchecks the other button. This could be also used for more than 2 ToggleButtons.
If you can be certain there are no other coontrols than ToggleButtons, you could also go for a foreach loop without the type-check.
public void ToggleButtonChecked(object sender, RoutedEventArgs e)
{
ToggleButton btn = sender as ToggleButton;
if (btn == null)
return;
Panel container = btn.Parent as Panel;
if (container == null)
return;
for (int i = 0; i<container.Children.Count; i++)
{
if (container.Children[i].GetType() == typeof(ToggleButton))
{
ToggleButton item = (ToggleButton)container.Children[i];
if (item != btn && item.IsChecked == true)
item.IsChecked = false;
}
}
}
XAML
<ToggleButton x:Name="tb1" Checked="ToggleButtonChecked"/>
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 have some code which uses a form. The form is bound to my class, FormData. I have binding working well and updating my formData (local instance), but when I try to change the value of one of the variables in formData on button click/LostFocus trigger, it doesn't update.
Here's my relevant XAML:
<TextBox x:Name="friendly_name_textBox"
Style="{StaticResource TextErrorStyle}"
Text="{Binding
PrimaryUserName,
Mode=TwoWay,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True}"
HorizontalAlignment="Left"
Margin="0,75,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"/>`
The button trigger (which does get run):
private void Button_Click(object sender, RoutedEventArgs e)
{
formData.PrimaryUserName = "TEST";
}
And my FormData code:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
}
}
}
You need to implement the INotifyPropertyChanged interface and raise the PropertyChanged event in your formData class:
public class formData : INotifyPropertyChanged
{
private string primaryUserNameValue;
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if (primaryUserNameValue != value)
{
primaryUserNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Class needs to implement INotifyPropertyChanged, so that the target knows if the source property changes:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
It's really easy, please have a look at the documentation and adjust your code accordingly. Your Property would have to look like this:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
OnPropertyChanged("PrimaryUserName");
}
}
}
But you also need the event and onPropertyChanged function to make it work.
Happy Coding!
I have two different objects that are pointing at each other. The first object represents a division in a company. That object has two collection: Employees, which is all the employees working in the division and Project, which is all the special projects that are in progress within that division. So the first object looks like this:
public class Division : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
ObservableCollection<Employee> _employees;
ObservableCollection<Project> _projects;
public Division()
{
Employees = new ObservableCollection<Employee>();
Projects = new ObservableCollection<Project>();
}
public ObservableCollection<Employee> Employees
{
get { return _employees; }
set
{
if (_employees != value)
{
_employees = value;
PropertyChanged(this, new PropertyChangedEventArgs("Employees"));
}
}
}
public ObservableCollection<Project> Projects
{
get { return _projects; }
set
{
if (_projects != value)
{
_projects = value;
PropertyChanged(this, new PropertyChangedEventArgs("Projects"));
}
}
}
public void AddNewProject()
{
this.Projects.Add(new Project(this));
}
}
Notice that when adding a new project to the division, I pass a reference to the division into that project, which looks like this:
public class Project : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
string _projectName;
DateTime _deadline = DateTime.Now;
Division _division;
ObservableCollection<Employee> _members;
public Project()
{
Members = new ObservableCollection<Employee>();
}
public Project(Division div)
{
Members = new ObservableCollection<Employee>();
Division = div;
}
public string ProjectName
{
get { return _projectName; }
set
{
if (_projectName != value)
{
_projectName = value;
PropertyChanged(this, new PropertyChangedEventArgs("ProjectName"));
}
}
}
public DateTime Deadline
{
get { return _deadline; }
set
{
if (_deadline != value)
{
_deadline = value;
PropertyChanged(this, new PropertyChangedEventArgs("Deadline"));
}
}
}
public Division Division
{
get { return _division; }
set
{
if (_division != value)
{
if (_division != null)
{
_division.Employees.CollectionChanged -= members_CollectionChanged;
}
_division = value;
if (_division != null)
{
_division.Employees.CollectionChanged += members_CollectionChanged;
}
PropertyChanged(this, new PropertyChangedEventArgs("Division"));
}
}
}
public ObservableCollection<Employee> Members
{
get { return _members; }
set
{
if (_members != value)
{
if (_members != null)
{
_members.CollectionChanged -= members_CollectionChanged;
}
_members = value;
if (_members != null)
{
_members.CollectionChanged += members_CollectionChanged;
}
PropertyChanged(this, new PropertyChangedEventArgs("Members"));
}
}
}
public ObservableCollection<Employee> AvailableEmployees
{
get
{
if (Division != null){
IEnumerable<Employee> availables =
from s in Division.Employees
where !Members.Contains(s)
select s;
return new ObservableCollection<Employee>(availables);
}
return new ObservableCollection<Employee>();
}
}
void members_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
PropertyChanged(this, new PropertyChangedEventArgs("AvailableEmployees"));
}
}
The reason I'm doing it like this is, that the project could have any type of team working on it, but only from within the division. So, when building a dashboard for the division, the manager could select any of the employees to that project but without putting in an employee that is already assigned to it. So, the AvailableEmployees property in the project object always keeps track of who is not already assigned to that project.
The problem I'm having is how to translate this into a UI. The experiment I've done so far looks like this:
<UserControl x:Class="Test.Views.TestView"
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"
mc:Ignorable="d"
xmlns:local="clr-namespace:Test.Views"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<ListBox ItemsSource="{Binding Div.Projects}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="Transparent"
BorderThickness="0, 0, 0, 2"
BorderBrush="Black"
Margin="0, 0, 0, 5"
Padding="0, 0, 0, 5">
<StackPanel>
<TextBox Text="{Binding ProjectName}"/>
<ListBox ItemsSource="{Binding Members}">
<ListBox.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:TestView}, Path=DataContext.AvailableEmployees}"
DisplayMemberPath="FirstName"
Text="{Binding FirstName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Add Employee to Project"
Command="{Binding RelativeSource={RelativeSource AncestorType=local:TestView}, Path=DataContext.AddEmployeeToProject}"
CommandParameter="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Add New Project"
Command="{Binding AddNewProject}" />
</StackPanel>
The view model associated with this view is as follows:
public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private Division _div;
public TestViewModel(Division div)
{
Div = div;
AddNewProject = new DelegateCommand(OnAddNewProject);
AddEmployeeToProject = new DelegateCommand<Project>(OnAddEmployeeToProject);
}
public DelegateCommand AddNewProject { get; set; }
public DelegateCommand<Project> AddEmployeeToProject { get; set; }
public Division Div
{
get { return _div; }
set
{
if (_div != value)
{
_div = value;
PropertyChanged(this, new PropertyChangedEventArgs("Div"));
}
}
}
private void OnAddNewProject()
{
Div.AddNewProject();
}
private void OnAddEmployeeToProject(Project proj)
{
var availables = proj.AvailableEmployees;
if (availables.Count > 0)
{
proj.Members.Add(availables[0]);
}
}
}
However, I cannot get the combobox for each employee in each project to work. It seems like the selected item/value is bound to the itemssource, and each time the combobox turns out blank. I've tried to do this also with SelectedValue and SelectedItem properties for the combobox, but none worked.
How do I get these two separated. Is there anything else I'm missing here?
OK. After so many experiments the best solution I came up with was to create my own user control that is composed of both a button and a combobox that imitate the behavior I was expecting of the combobox on it own.
First, I had a really stupid mistake in the model where both lists of members Project and Division contain the same instances of Employee, which makes the AvailableEmployees property buggy. What I really needed to do is to create a list of copies of employees in the Project instead of just references.
In any case, I created a new user control and called it DynamicSourceComboBox. The XAML of this control looks like this:
<Grid>
<Button x:Name="selected"
Content="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=SelectedValue}"
Click="selected_Click"/>
<ComboBox x:Name="selections"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=ItemsSource}"
DisplayMemberPath="{Binding RelativeSource={RelativeSource AncestorType=local:DynamicSourceComboBox}, Path=DisplayMemberPath}"
Visibility="Collapsed"
SelectionChanged="selections_SelectionChanged"
MouseLeave="selections_MouseLeave"/>
</Grid>
I have here a few bindings from the button and the combobox to properties in my user control. These are actually dependency properties. The code-behind of my user control looks like this:
public partial class DynamicSourceComboBox : UserControl
{
public DynamicSourceComboBox()
{
InitializeComponent();
}
public object SelectedValue
{
get { return (object)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(DynamicSourceComboBox), new PropertyMetadata(null));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
ComboBox.ItemsSourceProperty.AddOwner(typeof(DynamicSourceComboBox));
public string DisplayMemberPath
{
get { return (string)GetValue(DisplayMemberPathProperty); }
set { SetValue(DisplayMemberPathProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty =
ComboBox.DisplayMemberPathProperty.AddOwner(typeof(DynamicSourceComboBox));
private void selected_Click(object sender, RoutedEventArgs e)
{
selected.Visibility = Visibility.Hidden;
selections.Visibility = Visibility.Visible;
selections.IsDropDownOpen = true;
}
private void selections_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
selections.Visibility = Visibility.Collapsed;
selected.Visibility = Visibility.Visible;
selections.IsDropDownOpen = false;
if (e.AddedItems.Count == 1)
{
var item = e.AddedItems[0];
Type itemType = item.GetType();
var itemTypeProps = itemType.GetProperties();
var realValue = (from prop in itemTypeProps
where prop.Name == DisplayMemberPath
select prop.GetValue(selections.SelectedValue)).First();
SelectedValue = realValue;
}
}
private void selections_MouseLeave(object sender, MouseEventArgs e)
{
selections.Visibility = Visibility.Collapsed;
selected.Visibility = Visibility.Visible;
selections.IsDropDownOpen = false;
}
}
These dependency properties imitate the properties with similar names in ComboBox but they are hooked up to the internal combobox and the button in a way that makes them behave together as a single complex combobox.
The Click event in the button hides it and present the combobox to make the effect of just a box that is opening. Then I have a SelectionChanged event in the combobox firing to update all the needed information and a MouseLeave event just in case the user doesn't make any real selection change.
When I need to use the new user control, I set it up like this:
<local:DynamicSourceComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType=ListBox}, Path=DataContext.AvailableEmployees}"
DisplayMemberPath="FirstName"
SelectedValue="{Binding FirstName, Mode=TwoWay}"/>
Of course, for all of it to work, I have to make a lot of hookups with PropertyChanged events in the models, so the Projects instance will know to raise a PropertyChanged event for AvailableEmployees any time a change is made, but this is not really the concern of this user control itself.
This is a pretty clunky solution, with a lot of extra code that is a bit hard to follow, but it's really the best (actually only) solution I could have come up with to the problem I had.
I have this code for my ListView:
<ListView x:Name="listme">
<ListView.ItemTemplate >
<DataTemplate >
<Grid>
...
<Button Background="{Binding ButtonColor}" x:Name="btnStar"
Click="btnStar_Click" Tag={Binding}>
<Image/>
<TextBlock Text="{Binding Path=all_like}" x:Name="liketext" />
</Button>
</Grid>
</DataTemplate >
</ListView.ItemTemplate >
</ListView >
I have 2 ListviewItems,each one has a "BtnStar" Button,each Button has a "liketext" TextBlock,one of those TextBlocks works only,per example when I click on btnStar of ListViewItem1 it modifies the TextBlock value of ListViewItem2's TextBlock,I can't modify the Text of TextBlock of ListViewItem1 when I click on BtnStar of ListViewItem1,this is my code:
ObservableCollection<Locals> Locals = new ObservableCollection<Locals>();
public async void getListePerSearch()
{
try
{
UriString2 = "URL";
var http = new HttpClient();
http.MaxResponseContentBufferSize = Int32.MaxValue;
var response = await http.GetStringAsync(UriString2);
var rootObject1 = JsonConvert.DeserializeObject<NvBarberry.Models.RootObject>(response);
foreach (var item in rootObject1.locals)
{
Item listItem = new Item();
if (listItem.all_like == null)
{
listItem.all_like = "0";
}
listme.ItemsSource = Locals;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var item = btn.Tag as Locals;
item.all_like = liketext.Text;
liketext.Text = (int.Parse(item.all_like) + 1).ToString();
}
Locals.cs:
public class Locals : INotifyPropertyChanged
{
public int id_local { get; set; }
public string all_like { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
so,how can I modify the value of the TextBlock when I click on the BtnStar Button of each ListViewItem
thanks for help
Well. first of all you need use Binding approach in xaml apps.
your class Locals implement INotifyPropertyChanged but is bad implemented.
Please check this example:
public string someProperty {get;set;}
public string SomeProperty
{
get
{
return someProperty;
}
set
{
someProperty =value;
NotifyPropertyChanged("SomeProperty");
}
}
in your textblock you have Text={Binding SomeProperty}
you need to add Mode= TwoWay
Text={Binding SomeProperty, Mode= TwoWay}
finally in your click method
btnStar_Click
you need to do something like this:
var btn = sender as Button;
var local= btn.DataContext as Local;
local.SomeProperty= "my new value"
If you have implemented correctly INotifyPropertyChanged in your model you will see the change in your UI.
that's all.
Please mark this answer if it's useful for you!
Best Regards.
In my user control I have this property:
public static DependencyProperty FooListProperty = DependencyProperty.Register(
"FooList", typeof(List<Problem>), typeof(ProblemView));
public List<Problem> FooList
{
get
{
return (List<Problem>)GetValue(FooListProperty);
}
set
{
SetValue(FooListProperty, value);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == FooListProperty)
{
// Do something
}
}
And since another window, I´m trying to set a value for the last user control:
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center" FooList="{Binding list}" />
And that window in load contains:
public List<Problem> list;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Some processes and it sets to list field
list = a;
}
But in XAML code, binding it isn't working. Don't pass the data. What am I wrong?
You can't bind to a Field in WPF, you'll have to change list to a property instead.
You call the Dependency Property FooList in your UserControl and ResultList in Xaml but I'm guessing that's a typo in the question.
You should implement INotifyPropertyChanged in the Window to let the Bindings know that the value has been updated.
I'm not sure if you have the correct DataContext set in the Xaml ProblemView, if you're unsure you can name the Window and use ElementName in the binding
<Window Name="window"
...>
<!--...-->
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center"
ResultList="{Binding ElementName=window,
Path=List}" />
<!--...-->
</Window>
Sample code behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
//...
private List<Problem> m_list;
public List<Problem> List
{
get { return m_list; }
set
{
m_list = value;
OnPropertyChanged("List");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}