WPF PropertyGrid doesn't apply property value - c#

I'm using this
property control for WPF from Denis Vuyka.
I have the problem that it doesn't apply the new value of a property, if I don't press the TAB key.
So if I change a property in the property grid and then click the OK button, the property has still the previous value.
Sample code to reproduce:
public partial class MainWindow : Window
{
DataObject dataObject = new DataObject();
public MainWindow()
{
InitializeComponent();
propertyGrid.SelectedObject = dataObject;
}
private void OnOK(object sender, RoutedEventArgs e)
{
MessageBox.Show("Value of test is " + dataObject.test);
}
}
class DataObject
{
public int test { get; set; }
public int test2 { get; set; }
}
<Window x:Class="PropGridTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pg="http://schemas.denisvuyka.wordpress.com/wpfpropertygrid"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" IsDefault="True" Click="OnOK">OK</Button>
<pg:PropertyGrid x:Name="propertyGrid" Grid.Row="1">
</pg:PropertyGrid>
</Grid>
</Window>
Just type a number into property test and then click the OK button.
Does anybody know a workaround for this problem?
This is what I tried in OnOK so far to no avail:
propertyGrid.Focus();
propertyGrid.MoveFocus(new System.Windows.Input.TraversalRequest(System.Windows.Input.FocusNavigationDirection.Next));
System.Windows.Forms.SendKeys.SendWait("{TAB}");

You'd need to edit the source code and change the binding on the text editor so that it uses UpdateSourceTrigger=PropertyChanged.
To find the area of source that needs updating you can use Snoop to inspect the control.
Get your application running, fire up snoop, pick your application from the drop down menu on the Snoop tool, and click the binoculars. Now if you hold shift and ctrl keys while you hover the cursor over the control you'll be able to see its type and all of its properties.
After that you just need to search the solution to find that type and edit the binding in the XAML. Take a look at this page for info on how to use the UpdateSourceTrigger binding property.

I do not know exactly for this grid (I use this one), but I have the same problem there. It seems to be a common problem. Try to remove focus from PropertyGrid to another control before selecting new object ot clearing selected object property. For example:
public static void UpdatePropertyGridObjects(object objToSelect)
{
Components.DockManager.Focus();
Components.PropertyGrid.SelectedObject = objToSelect;
}

Related

Property of ObservableCollection type generates error in designer

I have a couple of WPF projects I am working on using Visual Studio 2019 Community. One is a control library and one is an application using the control library.
In the control library, I have a class ExteraWindow derived from a System.Windows.Window class. The relevant code for this class looks something like this:
namespace Extera.Presentation.Control
{
public class Window : System.Windows.Window
{
static Window()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Window),
new System.Windows.FrameworkPropertyMetadata(typeof(Window)));
}
public Window()
: base()
{
// initialize the MenuItems collection
MenuItems.CollectionChanged += Items_CollectionChanged;
}
public ObservableCollection<MenuItem> MenuItems { get; } = new ObservableCollection<MenuItem>();
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// some code here
}
// some more class members here
}
}
The code for the MenuItem class looks like this:
public class MenuItem
{
public MenuItem()
{
Caption = "";
Title = "";
}
public string Title { get; set; }
public string Caption { get; set; }
}
The ExteraWindow class is used in the app like this:
<control:Window x:Class="App.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:Extera.App.ProjectManager"
xmlns:control="clr-namespace:Extera.Presentation.Control;assembly=Extera.Presentation.Control"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<control:ExteraWindow.MenuItems>
<control:MenuItem Title="aaaa" Caption="bbbb" />
</control:ExteraWindow.MenuItems>
<Border BorderBrush="White" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Foreground="White">a;osidjflsodjfo;sdjg</Label>
</Grid>
</Border>
</control:ExteraWindow>
The code compiles OK and works OK. If I set a breakpoint in the Items_CollectionChanged method of the ExteraWindow class, the breakpoint gets hit once, and that is for adding a MenuItem element with the Title "aaaa" and the Caption "bbbb", which is exactly what I expected.
But the problem is the designer for the MainWindow. Here is a screenshot:
So, my question is: how do I clear this error? The compiler certainly does not complain about anything, so this is a designer only problem. Are there any attributes that I need to decorate my MenuItems property with? Or anything else I missed?
Thank you,
Tibi.
Edit:
Noticed that the MenuItem class was posted only with a constructor, missing the properties for Title and Caption. I added them now, for completeness.
MenuItems property does not have a 'set' in it, but you are trying to 'set' it from xaml,
so maybe that is the reason.

Trying to get add something to an Listview when I press a button

I'm working to figure out data binding and item sources with WPF and C#, and I'm missing something when it comes to making the proper binding connections. The result is a runtime error:
Cannot find source for binding with reference 'ElementName=SettingsWindow'. BindingExpression:Path=teams; DataItem=null; target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
The code itself is pretty straightforward (I think):
SettingsWindow.xaml:
<Window x:Class="Bridge.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Settings" Height="480" Width="600">
...
<Button Grid.Column="0" Grid.Row="0" Content="Add Team" Click="ClickAddTeam"/>
<ListView ItemsSource="{Binding}" Grid.Column="0" Grid.Row="1">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Foreground" Value="{Binding team.color}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
...
</Window>
SettingsWindow.xaml.cs:
public partial class SettingsWindow : Window
{
public TeamList teams { get; set; }
public SettingsWindow()
{
teams = TeamManager.Instance().teamList; // persists in a different class
this.DataContext = this.teams;
InitializeComponent();
}
private void ClickAddTeam(object sender, RoutedEventArgs e)
{
TeamManager manager = TeamManager.Instance();
Team toAdd = manager.GetSampleTeam(teams.Count);
Console.WriteLine(toAdd.ToString());
teams.Add(toAdd);
if(teams.Count == manager.sampleTeams.Count)
(sender as Button).IsEnabled = false;
}
}
Team.cs:
namespace DataTypes
{
public class Team
{
public string name;
public Brush color;
public Team(string name, Brush color)
{
this.name = name;
this.color = color;
}
}
}
Now working:
The WriteLine in ClickAddTeam is printing the right data, so I know it's retrieving the Team object correctly and that the MyTeamList teams object is getting stuff added to it. The button is also being disabled after the appropriate number of clicks. The ListBox, however, stays empty the entire time.
Next step:
I'm trying to get the strings in the ListBox to be team.name rather than "DataTypes.Team", and for the text's foreground to be the team's color. How do I grab a specific property of the bound element?
Any help would be greatly appreciated!
Binding only works with public properties. Try to declare teams as property instead of field/member:
public MyTeamList teams { get; set; }
// OP note: This part below wouldn't compile, giving me a member names cannot be the same as their enclosing type error.
and because you're binding by ElementName, you need to name you Window properly :
<Window x:Class="Bridge.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="SettingsWindow"
Title="Settings" Height="480" Width="600">
[OP Note]
Just changing that line to ItemsSource="{Binding}" works for displaying stuff, but every item displays as "DataType.Team" instead of a proper string. That's the next thing to figure out.
[/OP Note]
It is supposed to be one question per question
You need to set the DataContext
this.DataContext = this;
ItemsSource="{Binding Path=TeamList}" DisplayMemberPath=Name
And name needs to be a public property (get)

WPF Custom Control Error

I have some problems with a WPF custom control, I'm trying to make it work but just don't get it:
Here is my problem, I'm creating a simple custom control that's almost the same to a TextBox. This control has a dependency property named "Texto", and the binding between the XAML and back-code of the custom control works fine, here is the code:
<UserControl x:Class="WpfCustomControlLibrary1.UserControl1"
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"
d:DesignHeight="47" d:DesignWidth="147">
<Grid Height="43" Width="142">
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,8,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Texto}"/>
</Grid>
And the dependency property code:
public static readonly DependencyProperty TextoProperty = DependencyProperty.Register("Texto", typeof(string), typeof(UserControl1));
public string Texto
{
get
{
return (string)GetValue(TextoProperty);
}
set
{
SetValue(TextoProperty, value);
}
}
Ok, now the problem: When I use this control in other windows I try to bind the "Texto" property to a viewmodel (as simple as everything else) but the property on the view model just dont change:
The ViewModel code:
public class ViewModelTest
{
public string SomeText { get; set; }
}
And the code of the applicatoin Window:
public ViewModelTest test;
public Window1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show(test.SomeText);
MessageBox.Show(uc.Texto);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
test = new ViewModelTest();
this.DataContext = test;
}
And the binding with the property of the view model:
<my:UserControl1 HorizontalAlignment="Left" Margin="27,12,0,0" Name="uc" VerticalAlignment="Top" Texto="{Binding Path=SomeText,Mode=TwoWay}"/>
Just for make it clearer, if I write "Hello" in the custom control and then I push the "button1", the first message shows nothing and the second message shows "Hello".
As you can see I'm fairly new into this, so I hope some of you can help me. Thanks.
Your binding Texto="{Binding SomeText}" works fine, the problem is the rebinding from your user control to the inner textbox. Remember binding will ALWAYS, if not modified, refere to the DataContext. But your DataContext doesn't contain the property Texto. Your control has that, To refere to that you need something called TemplateBinding, but this only works when you are in a ControlTemplate. Which you aren't so what is the solution?
You can use a special form of binding, by changing the source from the DataContext to a control with a given name: First give your UserControl a name
<UserControl x:Class="WpfCustomControlLibrary1.UserControl1"
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"
x:Name="mainControl"
d:DesignHeight="47" d:DesignWidth="147">
and now change the binding to refere to the control, not the DataContext of the control anymore.
<TextBox Text="{Binding ElementName=mainControl, Path=Texto}"/>
Now your ViewModel binds to your user control and the content of the user control binds to the user controls Texto property.
Also one minor thing, what you called custom control, is in fact a user control, custom controls are something else.

Wpf - Update custom user control at runtime

hey guys i created an wpf application with custom usercontrol , the problem is that i am unable to update or say change the properties of custom control at runtime,
-->heres a code of user control
File:usercontrol.xaml
<UserControl x:Class="ExampleWpf.UserControlExample"
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"
Width="Auto" Height="Auto">
<Rectangle Width="60" Height="200" Fill="#FFB65959" Name="Box1"></Rectangle>
File:usercontrol.xaml.cs
namespace ExampleWpf{
public partial class UserControlExample : UserControl
{
public UserControlExample()
{
InitializeComponent();
}
public double Box1Width
{
get { return (Box1.Width); }
set { Box1.Width = value; }
}
}
--->Heres a simple code for Wpfapplication
File:Mainwindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
UserControlExample uc = new UserControlExample();
uc.Box1Width = 100;
}
}
All i wanted to change the width of a custom control at run time when user click on the button----> but unfortunately i doesn't succeed
please help me out
You are creating a new instance of your user control and not doing anything with it.
You should be finding the existing instance of the control and updating that.
You should also implement this type of behaviour through Dependency Properites. This gives you all sorts of advantages as outlined in the tutorial, but the main one here is that you can set properties like this at runtime.
Use the instance you placed in your xaml (f.e. through the Name-property), like so:
<uc:UserControlExample Name="myUC"/>
myUC.Box1Width = 100;
Another problem could be, that you just change the Width of the UC's rectangle. So lets say the UC has a Width of 50 and you call uc.Box1Width=100, the UC has still a Width of 50, so it wont change anything.

C# .NET4 WPF - Set combobox selecteditem from a string

Im all new in the world of C# and .net platform,so please be easy on me.
This forum helped me in a few problems that i came into while doing my project,but im now stuck on this for a few days.
What i'm trying to achieve is to set the selecteditem of a combobox by passing a string to it.
The scenario is :
I have a datatable and im setting the combo's itemssource to that datatable.DefaultView.
Also i set the DisplayMemberPath of the combo,and so far everything is ok,the items show up in the combobox.
Beside this i have a string with some value that i have inside the combobox too.
So i'm trying to set the selecteditem of the combo like this :
combo.SelectedItem = mystring;
As you can guess,it's not working. Strangely,when i do this:
combo.Items.Add(mystring);
combo.SelectedItem = mystring;
It's working. So this is why I'm confused!
EDIT:
I just found the solution :
combo.ItemsSource = datatable.DefaultView;
combo.DisplayMemberPath = "yourpath";
combo.SelectedValuePath = "yourpath";
combo.SelectedValue = mystring;
So the trick was to set the SelectedValuePath and the SelectedValue properties.
I don't know is this a good programming practice,but this does exactly what i needed.
You're doing something wrong.
Here's a demo app that shows this (the project should be named "StringCombo").
<Window
x:Class="StringCombo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
ResizeMode="CanResize">
<Window.DataContext>
<ViewModel
xmlns="clr-namespace:StringCombo" />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox
Name="OldeFashonedCombo" />
<Button
Grid.Column="1"
Content="Select Olde Waye"
Click="Button_Click" />
<ComboBox
Grid.Row="1"
ItemsSource="{Binding Strings}"
SelectedItem="{Binding SelectedString}" />
<Button
Grid.Row="1"
Grid.Column="1"
Content="Select New Way"
Command="{Binding SelectString}" />
</Grid>
</Window>
We've got two combos and two buttons. One uses the old winforms method of codebehind to manipulate the combo, and the other uses the new MVVM pattern.
In both scenarios, the user clicks the button, it sets the combo's SelectedValue, and the combo updates on the ui.
Here's the codebehind version:
public MainWindow()
{
InitializeComponent();
OldeFashonedCombo.Items.Add("One");
OldeFashonedCombo.Items.Add("Two");
OldeFashonedCombo.Items.Add("Three");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OldeFashonedCombo.SelectedItem = "Two";
}
Notice I'm not using the same "instance" of "Two"; there is no need as strings are "interned," or the same instance is automatically reused, in the .NET platform. object.ReferenceEquals("Two","Two") is always true.
So, I add strings to the Items collection, and when the button is clicked I set the SelectedItem to "Two". SelectedItem is the actual instance within the Items collection that should be selected. SelectedValue is the display value; you can select by this IIRC, but I wouldn't do that as a best practice.
Here's the MVVM version:
public sealed class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> Strings { get; private set; }
public ICommand SelectString { get; private set; }
public string SelectedString { get; set; }
public ViewModel()
{
Strings = new ObservableCollection<string>();
Strings.Add("Foo");
Strings.Add("Bar");
Strings.Add("Baz");
SelectString = new SelectStringCommand
{
ExecuteCalled = SelectBar
};
}
private void SelectBar()
{
SelectedString = "Bar";
// bad practice in general, but this is just an example
PropertyChanged(this, new PropertyChangedEventArgs("SelectedString"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
/// <summary>
/// ICommands connect the UI to the view model via the commanding pattern
/// </summary>
public sealed class SelectStringCommand : ICommand
{
public Action ExecuteCalled { get; set; }
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
ExecuteCalled();
}
}
Again, because of interning, I do not have to use the same "instance" of the string. To see how the ViewModel connects to the UI, check the bindings on the ComboBox and the Button (If you haven't looked into it yet, I'd strongly suggest ditching codebehind for MVVM. It may take a little more effort to figure it out, but its MUCH better in the long run).
ANYHOW, if you run this app you'd see that BOTH versions work as expected. When you click the button, the combo box is updated properly. This suggests that your code is wrong in some other way. Not sure what, as you haven't given us enough detail to determine this. But if you run the sample and compare it closely with your code, you might be able to figure this out.
I think using the findby will work so something like
combo.ClearSelection();
combo.Items.FindByValue(mystring).Selected = true;

Categories