I´m trying use RadioButton with statement IF
in short: I created a WPF application in which I want to select one of the four options and then click the button to open another window.I'd like to make sure the user clicked one off.
private void Button_Click(object sender, RoutedEventArgs e)
{
if (here I don't know how to create a condition)
{
MessageBox.Show("Musíš zvolit, jednu z početních operací!");
}
else
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
}
RadioButton == unchecked I thought of this condition but it does not go as well as the possibility of or(||)
as seen from the code I use error message in if and else opens another window
I'll be happy for any advice
Thank you
edit:
solution though not very nice
private void Button_Click(object sender, RoutedEventArgs e)
{
if (scitani.IsChecked.HasValue && scitani.IsChecked.Value)
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
else if (odcitani.IsChecked.HasValue && odcitani.IsChecked.Value)
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
else if (nasobeni.IsChecked.HasValue && nasobeni.IsChecked.Value)
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
else if (deleni.IsChecked.HasValue && deleni.IsChecked.Value)
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
else
{
MessageBox.Show("Musíš zvolit, jednu z početních operací!");
}
}
I made a sample source for you by using MVVM pattern.
👉Github
Structure
First,
you better use ListBox, that contains RadioButton as ListBoxItem.
MainMenuViewModel.cs
public class MainMenuViewModel : ObservableObject
{
private List<RadioItem> _radios;
public List<RadioItem> Radios
{
get { return _radios; }
set { _radios = value; OnPropertyChanged(); }
}
private RadioItem _currentRadio;
public RadioItem CurrentRadio
{
get { return _currentRadio; }
set { _currentRadio = value; OnPropertyChanged(); }
}
...
}
MainMenuResource.xaml
<Style TargetType="{x:Type ListBox}" x:Key="LBX">
<Setter Property="ItemContainerStyle" Value="{StaticResource LBXI}"/>
<Setter Property="ItemsSource" Value="{Binding Radios}"/>
<Setter Property="SelectedItem" Value="{Binding CurrentRadio}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
Radios binds to ItemsSource in ListBox, and CurrnetRadio binds to SelectedItem.
Second,
by using Command, you can close the MainWindow and show the SubWindow.
MainMenuViewModel.cs
public class MainMenuViewModel : ObservableObject
{
...
public ICommand ClickCommand { get; set; }
public MainMenuViewModel()
{
ClickCommand = new RelayCommand<Window>(Click);
...
}
private void Click(Window obj)
{
if (CurrentRadio.DisplayName == "Radio 1")
{
obj.DialogResult = true;
}
else
{
MessageBox.Show("Select another one!");
}
}
}
MainMenuResource.xaml
<Style TargetType="{x:Type Button}" x:Key="BTN.MAIN">
<Setter Property="Command" Value="{Binding ClickCommand}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
...
</Style>
Finally,
Because you need to control multiple windows in the application, it is common to treat them with the OnStartup method.
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow main;
SubWindow sub;
this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
main = new MainWindow();
main.ShowDialog();
if (main.DialogResult == true)
{
sub = new SubWindow();
sub.ShowDialog();
}
Environment.Exit(0);
}
}
you should reach your control
XAML:
<RadioButton x:Name="myRadioButton" />
CS:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (myRadioButton.IsChecked.HasValue && myRadioButton.IsChecked.Value)
{
MessageBox.Show("Musíš zvolit, jednu z početních operací!");
}
else
{
uroven lvl = new uroven();
lvl.Show();
this.Close();
}
}
Related
I would like to trigger the method SelectAllText() when the textbox background color is red. How can I bind to code behind.
xaml:
<TextBox Grid.Column="1" Grid.Row="0" Text="Text" MouseEnter="Test1MouseEnter" Background="{Binding TxtBoxcolor, Mode=OneWay}" Name="txbName">
<TextBox.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBox.Background" Value="Red">
<!--Trigger code behind-->
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
code behind:
public void SelectAllText()
{
txbName.SelectAll();
}
It's possible in your case to handle Changed event on the background in the code behind?
txbName.Background.Changed += Background_Changed;
and in the Background_Changed
private void Background_Changed(object sender, EventArgs e)
{
var brush = sender as Brush;
if(brush!=null)
{
if(brush == Brushes.Red)
{
txbName.SelectAll();
}
}
}
Here's a way to do this with an attached event. It can only handle raising change events for one property per control. To raise events on value changes for multiple properties, you'd need an attached property that's a collection of some object with a property name and an event, which would be more complicated to write. This really just demonstrates the concept, but it's sufficient for the specific problem you have in front of you right now.
public static class PropertyChange
{
public static readonly RoutedEvent PropertyChangeEvent =
EventManager.RegisterRoutedEvent("PropertyChangeEvent", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(PropertyChange));
public static void AddPropertyChangeEventHandler(DependencyObject d, RoutedEventHandler handler)
{
var uie = d as UIElement;
if (uie != null)
{
uie.AddHandler(PropertyChange.PropertyChangeEvent, handler);
}
}
public static void RemovePropertyChangeEventHandler(DependencyObject d, RoutedEventHandler handler)
{
var uie = d as UIElement;
if (uie != null)
{
uie.RemoveHandler(PropertyChange.PropertyChangeEvent, handler);
}
}
#region PropertyChange.PropertyName Attached Property
public static String GetPropertyName(UIElement obj)
{
return (String)obj.GetValue(PropertyNameProperty);
}
public static void SetPropertyName(UIElement obj, String value)
{
obj.SetValue(PropertyNameProperty, value);
}
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.RegisterAttached("PropertyName", typeof(String), typeof(PropertyChange),
new PropertyMetadata(null, PropertyName_PropertyChanged));
private static void PropertyName_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as UIElement;
var oldProperty = e.OldValue as String;
var newProperty = e.NewValue as String;
if (oldProperty != null)
{
var dpd = DependencyPropertyDescriptor.FromName(oldProperty, d.GetType(), d.GetType());
dpd.RemoveValueChanged(d, PropertyChangedHandler);
}
if (newProperty != null)
{
var dpd = DependencyPropertyDescriptor.FromName(newProperty, d.GetType(), d.GetType());
dpd.AddValueChanged(d, PropertyChangedHandler);
}
}
private static void PropertyChangedHandler(object component, EventArgs args)
{
var uie = component as UIElement;
uie.RaiseEvent(new RoutedEventArgs(PropertyChange.PropertyChangeEvent, uie));
}
#endregion PropertyChange.PropertyName Attached Property
}
Demo
XAML
<TextBox
Width="100"
x:Name="TestTextBox"
Text="Blah blah blah"
local:PropertyChange.PropertyName="Background"
local:PropertyChange.PropertyChangeEvent="TestTextBox_PropertyChangeEvent"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Code behind
private void TestTextBox_PropertyChangeEvent(object sender, RoutedEventArgs e)
{
var tb = (TextBox)sender;
var x = tb.Background as SolidColorBrush;
// Instead of examining the background color, I would much prefer to look directly
// at the validation: What happens if you decide to change the error background color
// to a darker shade of red? Or a GradientBrush? A cosmetic decision like that should
// not affect program behavior.
//
// But you didn't give any clues about how your validation is implemented, so that's
// your problem not mine.
if (x != null && x.Color == Colors.Red)
{
tb.Focus();
tb.SelectAll();
}
}
I am learning WPF and MVVM, and have written a simple Type-Select List box in WPF as a learning practice program.
Though it works,I have three questions which are as under:-
1)How to set the Text property of TxtMail via DataBinding?Currently it messes up with other logic if i set Text property in XAML via DataBinding.I do not want to set the text property of Txtmail directly from Code-Behind,while setting the same via DataBinding in XAML messes up things owing to my limited understanding of the subject.
2)The ItemSource of ListBox named AllMatching is being set from Code-Behind since it is changing programatically with text search patterns.How can i set it from XAML?
3)Is there a way i can remove the logic of GUI Control Events and include the same in XAML?
The entire code is as under:-
ViewModel:
public class VM_Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public List<DM_Data> AllData;
public List<DM_Data> DynamicData;
public Visibility p_ListVisibility = Visibility.Collapsed;
private DM_Data _currentRec;
public DM_Data CurrentRec
{
get { return _currentRec; }
set { _currentRec = value; RaisePropertyChangedEvent("CurrentRec"); }
}
public VM_Data()
{
LoadData();
}
public int ID
{
get { return p_ID; }
set
{
if (p_ID != value)
{
RaisePropertyChangedEvent("ID");
p_ID = value;
}
}
}
public double SP
{
get { return p_SP; }
set
{
if (p_SP != value)
{
RaisePropertyChangedEvent("SP");
p_SP = value;
}
}
}
public double CP
{
get { return p_CP; }
set
{
if (p_CP != value)
{
RaisePropertyChangedEvent("CP");
p_CP = value;
}
}
}
public Visibility ListVisibility
{
get { return p_ListVisibility; }
set
{
p_ListVisibility = p_ListVisibility != value ? value : p_ListVisibility;
RaisePropertyChangedEvent("ListVisibility");
}
}
public string Name
{
get { return p_Name; }
set
{
if (p_Name != value)
{
RaisePropertyChangedEvent("Name");
p_Name = value;
}
}
}
private void LoadData()
{
AllData = new List<DM_Data>();
DynamicData = new List<DM_Data>();
string[] strNames = "Jatinder;Shashvat;shashikala;shamsher;shahid;justin;jatin;jolly;ajay;ahan;vijay;suresh;namita;nisha;negar;zenith;zan;zen;zutshi;harish;hercules;harman;ramesh;shashank;mandeep;aman;amandeep;amarjit;asim;akshay;amol;ritesh;ritivik;riz;samana;samaira;bhagwandass;bhagwan;bhawna;bhavna".Split(';');
for(int i=0;i<=strNames.GetUpperBound(0);i++)
{
DM_Data NewRec = new DM_Data();
NewRec.CP = new Random().Next(200, 400);
NewRec.SP = new Random().Next(1, 10);
NewRec.ID = i + 1;
NewRec.Name = strNames[i];
AllData.Add(NewRec);
}
AllData = DynamicData = AllData.OrderBy(item => item.Name).ToList();
}
private void RaisePropertyChangedEvent(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
DataModel:
public class DM_Data
{
public int p_ID;
public double p_SP, p_CP;
public string p_Name;
public Visibility p_ListVisibility = Visibility.Visible;
public int ID
{
get { return p_ID; }
set { p_ID = value; }
}
public Visibility ListVisibility
{
get { return p_ListVisibility; }
set { p_ListVisibility = value; }
}
public double SP
{
get { return p_SP; }
set { p_SP = value; }
}
public double CP
{
get { return p_CP; }
set { p_CP = value; }
}
public string Name
{
get { return p_Name; }
set { p_Name = value; }
}
}
MainWindow.Xaml
<Window x:Class="TypeSelect.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TypeSelect"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Canvas>
<TextBox x:Name="TxtMail" Width="244" FontSize="14" Canvas.Left="36" Canvas.Top="34" Height="25" GotFocus="TxtMail_GotFocus" LostFocus="TxtMail_LostFocus" KeyUp="TxtMail_KeyUp" MouseUp="TxtMail_MouseUp" />
<ListBox x:Name="AllMatching" Width="{Binding ElementName=TxtMail,Path=Width}" MinHeight="10" MaxHeight="100" Canvas.Top="54" Canvas.Left="36" DisplayMemberPath="Name" SelectedItem="{Binding CurrentRec,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedValue="Name" SelectedValuePath="Name" Visibility="{Binding ListVisibility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" KeyUp="AllMatching_KeyUp" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" MouseUp="AllMatching_MouseUp" GotFocus="AllMatching_GotFocus" MouseDoubleClick="AllMatching_MouseDoubleClick" ForceCursor="True">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontFamily" Value="Arial Bold" />
<Setter Property="Background" Value="LightGray" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="18" />
</Trigger>
</Style.Triggers>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="LightGray"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGray"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="White" />
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Content="Test" x:Name="cmdtest" Click="cmdtest_Click"/>
</Canvas>
</StackPanel>
MainWindow.Xaml.cs
namespace TypeSelect
{
public partial class MainWindow : Window
{
VM_Data ViewModel;
DM_Data Existing;
public MainWindow()
{
InitializeComponent();
ViewModel = new VM_Data();
this.DataContext = ViewModel;
AllMatching.ItemsSource = ViewModel.DynamicData;
ViewModel.CurrentRec = Existing= ViewModel.AllData[new Random().Next(0, ViewModel.AllData.Count - 1)];
TxtMail.Text = (ViewModel.CurrentRec != null) ? ViewModel.CurrentRec.Name : "";
}
private void cmdtest_Click(object sender, RoutedEventArgs e)
{
}
private void TxtMail_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key.Equals(Key.Enter))
{
ViewModel.CurrentRec = (DM_Data)AllMatching.SelectedItem;
((TextBox)sender).Text = (ViewModel.CurrentRec != null) ? ViewModel.CurrentRec.Name : "";
//ViewModel.CurrentRec.SearchText = ViewModel.CurrentRec.Name;
ViewModel.ListVisibility = ViewModel.ListVisibility.Equals(Visibility.Collapsed) ? Visibility.Visible : Visibility.Collapsed;
if (ViewModel.ListVisibility.Equals(Visibility.Visible))
{
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
else
{
Existing = ViewModel.CurrentRec;
}
}
else if (e.Key.Equals(Key.Escape))
{
ViewModel.CurrentRec = Existing;
ViewModel.ListVisibility = Visibility.Collapsed;
((TextBox)sender).Text = (ViewModel.CurrentRec != null) ? ViewModel.CurrentRec.Name : "";
}
else if (e.Key.Equals(Key.Up))
{
AllMatching.SelectedIndex = AllMatching.SelectedIndex > 0 ? AllMatching.SelectedIndex - 1 : AllMatching.SelectedIndex;
ViewModel.CurrentRec = (DM_Data)AllMatching.SelectedItem;
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
else if (e.Key.Equals(Key.Down))
{
AllMatching.SelectedIndex = AllMatching.SelectedIndex < AllMatching.Items.Count ? AllMatching.SelectedIndex + 1 : AllMatching.SelectedIndex;
ViewModel.CurrentRec = (DM_Data)AllMatching.SelectedItem;
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
else
{
ViewModel.DynamicData = ViewModel.AllData.Where(item => item.Name.StartsWith(TxtMail.Text, StringComparison.OrdinalIgnoreCase)).ToList<DM_Data>();
AllMatching.ItemsSource = ViewModel.DynamicData;
ViewModel.CurrentRec = (AllMatching.Items.Count > 0) ? ViewModel.DynamicData.Where(item => item.Name.StartsWith(TxtMail.Text, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() : null;
if (AllMatching.Visibility.Equals(Visibility.Collapsed))
{
AllMatching.Visibility = Visibility.Visible;
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
}
}
private void TxtMail_GotFocus(object sender, RoutedEventArgs e)
{
DM_Data Existing = ViewModel.CurrentRec;
ViewModel.ListVisibility = Visibility.Visible;
}
private void TxtMail_LostFocus(object sender, RoutedEventArgs e)
{
ViewModel.ListVisibility = Visibility.Collapsed;
}
private void TxtMail_MouseUp(object sender, MouseButtonEventArgs e)
{
ViewModel.ListVisibility = Visibility.Visible;
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
private void AllMatching_MouseUp(object sender, MouseButtonEventArgs e)
{
ViewModel.CurrentRec = (DM_Data)AllMatching.SelectedItem;
ViewModel.ListVisibility = Visibility.Visible;
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
private void AllMatching_GotFocus(object sender, RoutedEventArgs e)
{
ViewModel.ListVisibility = Visibility.Visible;
}
private void AllMatching_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key.Equals(Key.Enter))
{
TxtMail.Text = ViewModel.CurrentRec.Name;
ViewModel.ListVisibility = Visibility.Collapsed;
}
}
private void AllMatching_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ViewModel.CurrentRec = (DM_Data)AllMatching.SelectedItem;
TxtMail.Text = (ViewModel.CurrentRec != null) ? ViewModel.CurrentRec.Name : "";
//ViewModel.CurrentRec.SearchText = ViewModel.CurrentRec.Name;
ViewModel.ListVisibility = ViewModel.ListVisibility.Equals(Visibility.Collapsed) ? Visibility.Visible : Visibility.Collapsed;
if (ViewModel.ListVisibility.Equals(Visibility.Visible))
{
AllMatching.ScrollIntoView(ViewModel.CurrentRec);
}
else
{
Existing = ViewModel.CurrentRec;
}
}
}
}
How to set the Text property of TxtMail via DataBinding?
Bind it to a source property of the view model:
<TextBox x:Name="TxtMail" Binding="{Binding Mail}" />
Mail is a property of the VM_Data class, just like Name.
The ItemSource of ListBox named AllMatching is being set from Code-Behind since it is changing programatically with text search patterns. How can i set it from XAML?
You should bind this one as well:
<ListBox x:Name="AllMatching" ItemsSource="{Binding DynamicData}" ...>
For this to work, DynamicData must be a property:
public List<DM_Data> DynamicData { get; set; }
If you are adding items dynamically to it, you should make it an ObservableCollection:
public ObservableCollection<DM_Data> DynamicData { get; set; }
Is there a way i can remove the logic of GUI Control Events and include the same in XAML?
XAML is a markup language. It is not a programming language. But you should look into commands: https://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/.
I have the following code creating a AutoCompleteBox (using WPFToolkit) that combines Companies and Employees into a single search feature. I am forced to use an ObservableCollection as the AutoCompleteBox does not know how to handle CompositeCollections (at least according to what I could find on the subject).
I would like to style the dropdown and textbox such that a Company's text is red and an Employee's text is green. I can set the text color for the entire dropdown as shown in the ResourceDictionary below but cannot find a way to use the DataTrigger (what I have below does not work).
I am not sure how to Bind the DataTrigger to the ModelType property of each object in SearchCollection.
EDIT:
After looking at the code again I realize that there is not a Property called ModelType exposed in the VM, the ModelType property is on each of the objects in SearchCollection. How can I bind to the ModelType of each object in the collection?
In XAML:
<toolkit:AutoCompleteBox Name="OmniSearchTextBox"
ItemsSource="{Binding SearchCollection}"
SelectedItem="{Binding Selection, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
KeyUp="OmniSearch_KeyUp"
MouseLeftButtonUp="OmniSearch_MouseLeftButtonUp"
IsTextCompletionEnabled="False"
FilterMode="Contains"
VerticalAlignment="Top" Margin="10,0" >
In VM:
public sealed class SearchViewModel : ViewModelBase
{
private ViewModelLocator _vmLocator;
private ObservableCollection<Company> _companyList;
private ObservableCollection<Employee> _employeeList;
/// <summary>
/// Initializes a new instance of the SearchViewModel class.
/// </summary>
public SearchViewModel()
{
try
{
_vmLocator = new ViewModelLocator();
_searchCompCollection.Add(companies);
_searchCompCollection.Add(employees);
foreach (Company co in CompanyList)
{
_searchCollection.Add(co);
}
foreach (Employee emp in EmployeeList)
{
_searchCollection.Add(emp);
}
}
catch (Exception ex)
{
}
}
private const string CompanyListPropertyName = "CompanyList";
public ObservableCollection<Company> CompanyList
{
get
{
return (_vmLocator.CompanyVM).CompanyList;
}
set
{
if (_companyList == value)
{
return;
}
_companyList = value;
RaisePropertyChanged(CompanyListPropertyName);
}
}
private const string EmployeeListPropertyName = "EmployeeList";
public ObservableCollection<Employee> EmployeeList
{
get
{
return (_vmLocator.EmployeeVM).EmployeeList;
}
set
{
if (_employeeList == value)
{
return;
}
_employeeList = value;
RaisePropertyChanged(EmployeeListPropertyName);
}
}
private ObservableCollection<ModelBase> _searchCollection = new ObservableCollection<ModelBase>();
public ObservableCollection<ModelBase> SearchCollection
{
get { return _searchCollection; }
}
private string _selection = null;
private string _origSelection = null;
public string Selection
{
get
{
return _selection;
}
set
{
if (_selection != value)
{
_origSelection = _selection;
_selection = value;
try
{
var item = _searchCollection.Single<Object>(x => x.ToString() == _selection);
this.SelectedObject = item;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
private object _selectedObject = null;
private object _origSelectedObject = null;
public object SelectedObject
{
get {
return _selectedObject;
}
set
{
if (_selectedObject != value)
{
_origSelectedObject = _selectedObject;
_selectedObject = value;
switch(_selectedObject.GetType().BaseType.Name)
{
case "Company":
RaisePropertyChanged("SelectedObject",
(Company)_origSelectedObject,
(Company)_selectedObject,
true);
break;
case "Employee":
RaisePropertyChanged("SelectedObject",
(Employee)_origSelectedObject,
(Employee)_selectedObject,
true);
break;
default:
break;
}
RaisePropertyChanged("SelectedObject", _origSelectedObject, _selectedObject, true);
}
}
}
}
In ResourceDictionary (ModelType is a property available in each Model that simply returns the class type, i.e., Company or Employee):
<Style TargetType="{x:Type toolkit:AutoCompleteBox}">
<Setter Property="Foreground" Value="DarkCyan" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SearchCollection.ModelType}"
Value="Company">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=SearchCollection.ModelType}"
Value="Employee">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
I am trying to dynamically populate a WPF tree by using a ViewModel, however, for some reason it's not working. Either the bindings either aren't properly or I am messing up somewhere in code behind.
Here's a sample of what I have.
In XAML I define my TreeView like so...
<TreeView DockPanel.Dock="Left" Width="200" DataContext="{Binding MessageTree}" ItemsSource="{Binding MessageTree}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="viewModel:Mail" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Subject}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In Code Behing i have...
private Mail MessageTree { get; set; }
And
using (var mail = new MailParser())
{
int count = mail.GetMessageCount(DateTime.Today.AddDays(-10), DateTime.Today.AddDays(1));
MessageTree = new Mail();
for (int i = count - 1; i >= 0; i--)
{
MailMessage msg = mail.RetrieveMessage(i);
if (msg != null)
{
MessageTree.Add(msg);
}
if (backgroundWorker != null)
{
decimal perc = (100.0m - (((i + 1.0m)*100.0m)/count));
backgroundWorker.ReportProgress((int) perc, "Recebendo mensagens... " + perc.ToString("N2") + "%");
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
break;
}
}
}
}
Mail is defined as
public sealed class Mail : INotifyPropertyChanged
{
private readonly ObservableCollection<Mail> _children;
private readonly MailMessage _msg;
private readonly Mail _parent;
private bool _isExpanded;
private bool _isSelected;
public Mail()
{
_msg = new MailMessage {Subject = "Empty"};
_parent = null;
_children = new ObservableCollection<Mail>();
}
public Mail(MailMessage msg, Mail parent = null)
{
_msg = msg;
_parent = parent;
_children = new ObservableCollection<Mail>();
}
public IEnumerable<Mail> Children
{
get { return _children; }
}
public string Subject
{
get { return _msg.Subject; }
}
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value != _isExpanded)
{
_isExpanded = value;
OnPropertyChanged();
}
if (_isExpanded && _parent != null)
_parent.IsExpanded = true;
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value != _isSelected)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void Add(MailMessage msg)
{
_children.Add(new Mail(msg, this));
OnPropertyChanged("Children");
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I can't find anything in it so different from examples found online that it wouldn't work. The Add method is incomplete, I still need some logic to decide whether to add them to the collection or to the collection of one of the collection members, but as is all my Mail objecys are beeing added to the collection but not showing up in the TreeView.
What totally obvious thing am i missing? Shouldn't the TreeView automaticly update as I add items to the collection?
What I want is for the TreeView to show The children of the MessageTree property, and those children's children.
EDIT: Couldn't see the whole thing on my phone - amended answer based on ability to actually see everything. :)
MOREEDIT: updated based on comments, let's start from scratch!
First off, if you're set on using the window/whatever as the datacontext, let's make it `INotifyPropertyChange...next, let's make "MessageTree" a collection of mails, not just a single one (it'll make binding semantics easier, trust me)
public class WhateverContainsTheTree : Window, INotifyPropertyChanged
{
public WhateverContainsTheTree()
{
this.Loaded += OnLoaded;
this._messageTree = new ObservableCollection<Mail>();
this.DataContext = this;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_worker = new BackgroundWorker();
_worker.DoWork += WorkerWorkin;
_worker.RunWorkerAsync();
}
private BackgroundWorker _worker;
private ObservableCollection<Mail> _messageTree;
public ObservableCollection<Mail> MessageTree
{
get { return _messageTree; }
set { _messageTree = value; RaisePropertyChanged("MessageTree"); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate {};
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void WorkerWorkin(object sender, DoWorkEventArgs e)
{
// obviously, change this to your stuff; I added a ctor so I could pass a string
Thread.Sleep(3000);
Console.WriteLine("Ok, setting message tree");
Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
(Action)(() =>
{
var mail1 = new Mail("Mail#1:Changed from background thread");
var mail2 = new Mail("Mail#2:Submail of mail #1");
var mail3 = new Mail("Mail#3:Submail of mail #2");
var mail4 = new Mail("Mail#4:Submail of mail #1");
var mail5 = new Mail("Mail#5:Submail of mail #4");
mail1.Children.Add(mail2);
mail1.Children.Add(mail4);
mail2.Children.Add(mail3);
mail4.Children.Add(mail5);
MessageTree.Add(mail1);
})
);
}
}
Also, like I'd said in the original response, let's slightly tweak Mail.Children:
public ObservableCollection<Mail> Children
{
get { return _children; }
}
And here's what I used for the treeview xaml:
<TreeView DockPanel.Dock="Left" Width="200" ItemsSource="{{Binding MessageTree}}">
<TreeView.ItemContainerStyle>
<Style TargetType="{{x:Type TreeViewItem}}">
<Setter Property="IsExpanded" Value="{{Binding IsExpanded, Mode=TwoWay}}" />
<Setter Property="IsSelected" Value="{{Binding IsSelected, Mode=TwoWay}}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="viewModel:Mail" ItemsSource="{{Binding Children}}">
<TextBlock Text="{{Binding Subject}}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
If this STILL doesn't work, I'll just paste in the whole LINQPad blob I put together to test this.
Without seeing the entire setup, I'm not positive but my guess would be that since MessageTree is a plain CLR property (rather than something that raises PropertyChanged or a DependencyProperty or something, that the binding is occurring before your MessageTree = new Mail(); call. When you set it to a new instance, the binding system isn't getting notified since it is a plain property.
Another potential issue is that you say that code is in the code-behind. Just using that Binding syntax won't pick up a property from the code-behind. It's possible that you're setting that up somewhere else in the code that you didn't show us. But generally you aren't going to be binding from the View to the code-behind, you'd be binding to a ViewModel that was used as the DataContext for the view itself.
Had to give a name to the TreeView (Tree) and then after
MessageTree = new Mail();
insert
Tree.ItemSource = MessageTree.Children;
I find this ugly but at least it works now. Thank you all for trying to help.
I am trying to change the code below from being an event setter to an attached property (just so I can clean up the code behind). I get an error in the setter saying the value cannot be null, but I don't see why yet.
Forgetting for a second whether this is a good idea or not, can someone help me get the attached property right?
Cheers,
Berryl
EventSetter (works but with code behind as shown)
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnPreviewMouseLeftButtonDown"/>
</Style>
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell;
cell.Activate();
}
Property setter (error)
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="begavior:DataGridCellProperties.SingleClickToEdit" Value="True"/>
</Style>
public class DataGridCellProperties
{
public static readonly DependencyProperty SingleClickToEditProperty =
DependencyProperty.RegisterAttached("SingleClickToEditProperty",
typeof(bool), typeof(DataGridCellProperties),
new PropertyMetadata(false, OnSingleClickToEditPropertyChanged));
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(DataGridCell))]
public static bool GetSingleClickToEdit(DataGridCell obj) { return (bool)obj.GetValue(SingleClickToEditProperty); }
public static void SetSingleClickToEdit(DataGridCell obj, bool value) { obj.SetValue(SingleClickToEditProperty, value); }
private static void OnSingleClickToEditPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (sender == null) return;
if ((bool)e.NewValue)
{
sender.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown_EditCell;
}
else
{
sender.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown_EditCell;
}
}
private static void OnPreviewMouseLeftButtonDown_EditCell(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell;
cell.Activate();
}
}
"SingleClickToEditProperty" in your d-prop registration should be "SingleClickToEdit".