I'm fairly new to WPF. I have the following radio button in my application
<Viewbox Height="30">
<RadioButton Content="B1" GroupName="InputBelt" IsChecked="{Binding RBChecked, Mode=TwoWay, FallbackValue=True}" VerticalAlignment="Center"/>
</Viewbox>
<Viewbox Height="30">
<RadioButton Content="B2" GroupName="InputBelt" IsChecked="{Binding RBChecked, Converter={StaticResource boolconverter}, Mode=TwoWay}" VerticalAlignment="Center"/>
</Viewbox>
I have defined datacontext in xaml file
<Window.DataContext>
<vm:TestViewModel />
</Window.DataContext>
The issue is when the page is loaded for the 1st time, everything is fine. But when I go to some other page in the application and comes back to this page, the application crashes due to stackoverflow exception.
I even tried adding datacontext locally in radiobutton tag but it isn't working either.
Property structure given below.
private bool _bRBChecked;
public bool RBChecked
{
get { return _bRBChecked; }
set
{
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
Upon investigating further, I found out that the RaisePropertyChanged of the binded property is being called too many times. This issue occurs only with the property binded to radio button. I have other controls which has two-way bind with other properties in the same page and it seems to work fine.
Now I have tried the below fix suggested in another stackoverflow question and it seems to be working.
set
{
if (_bRBChecked != value)
{
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
But I would like to know the root cause of this issue and why the property is being set so many times and find any alternate fix if possible. Please let me know if I am missing anything.
Any insight is highly appreciable.
Your change notification is not protected from recursion. Property A changing Property B, whose change changes Property A...
A simple solution is this:
set
{
if(value != _bRBChecked){
_bRBChecked = value;
RaisePropertyChanged("RBChecked");
}
}
Simply check if the value is actually a change, before you go tell everyone about it. This pattern is explicitly used in the Examples. I am 90% sure the Depdency Properties have a similar recursion protection, but it would not be the first time I was wrong.
I think it is fairly easy to figure this out, based on the fix you shared.
What happens in steps:
You set the new value in one of the radio buttons
The event is raised
Since it's two way binding, the binding of the second radio button sets the value again to the other radio button
The event is raised again due to 3
Go back to 1 as now the value is set again for the first radio button.
With your fix the value is not set (the setter it's not called) so the event is not triggered again.
Related
I'm trying to display different layouts in pivot items in a wp8.1 app (UNIAPP ). Ideally I would like to load different pages but since I could figure this out, I thought I'd try with the basics first as I'd use this before but for some reason I can't get this to work.
My pivot items are loaded dynamically based on the provided ViewModel
<Pivot.ItemTemplate>
<DataTemplate>
<controls:DataTemplateSelector Content="{Binding}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
</controls:DataTemplateSelector>
/DataTemplate>
</Pivot.ItemTemplate>
My resources are defined as follows within the same xaml page
<Page.Resources>
<DataTemplate x:Key="MyApp.ViewModel.PIDetailsVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
<DataTemplate x:Key="MyApp.ViewModel.PIListVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
</Page.Resources>
My DataTemplateSelector is defined as follows:
public class DataTemplateSelector : ContentControl
{
protected override void OnContentChanged(object oldContent,
object newContent)
{
ContentTemplate = this.FindResource<DataTemplate>(newContent.GetType
().FullName);
}
}
It is being triggered whenever I go to a new pivot item, but the ContentTemplate is always null.
The newContent.GetType().FullName returns the relevant viewmodel name which I can see being displayed in the relevant pivot.
One thing I noticed is that the DataTemplateSelector class (this) has no resources when I check it via this.Resources.count(), so it's obviously not finding them but how do I fix this?
UPDATE:
My DataTemplates are not getting loaded in my Pivot Items. There is obviously a problem with the .NET IDE as whenever I add or remove a from Content="{Binding}" it displays the button within the pivot item but that's within the IDE. Unfortunately, at run-time, it just displays the name of my viewmodel.
Thought the behaviour is erratic in the IDE, the fact that the button from my DataTemplate is displaying when messing around with the Content="{Binding<space>" would make you think that the code and xaml are correct but it's definitely not working at run-time.
Any idea what's wrong why my DataTemplates are not displaying in pivot item?
Thanks.
This is a partial answer. By this I mean that I did find a work-around to my problem but I did not resolve the issue itself.
My DataTemplateSelector which gets triggered whenever the pivot changes call a extension function called FindResource:
public static class ControlExtensions
{
public static T FindResource<T>(this DependencyObject initial,
string key) where T : DependencyObject
{
DependencyObject current = initial;
while (current != null)
{
if (current is FrameworkElement)
{
if ((current as FrameworkElement).Resources.
ContainsKey(key))
{
return (T)(current as FrameworkElement).Resources[key];
}
}
current = VisualTreeHelper.GetParent(current);
}
if (Application.Current.Resources.ContainsKey(key))
{
return (T)Application.Current.Resources[key];
}
return default(T);
}
}
For some strange reason, Windows Phone 8.1 (WinRT) does not like having the data templates in while it is not a problem in WP8/WP8.1 Silverlight.
As mentioned, this is unstable in the IDE where it sometimes displays the DataTemplate, and sometimes it doesn't depending on whether or not I add a space after the Binding keyword to the Content="{Binding}". One thing for sure is that it never works at run-time, well not at least not with the above code.
VisualTreeHelper.GetParent(current) always returns null no matter what. I've checked at debug time if I somehow could access the resources, but to no avail.
How did I fix it? Well, I moved my data templates to a resource dictionary
<DataTemplate x:Key="MyApp.ViewModel.PIDetailsVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
<DataTemplate x:Key="MyApp.ViewModel.PIListVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
The second I did this, the second part of my FindResources kicks in since the Current object is always null, no matter what
if (Application.Current.Resources.ContainsKey(key))
{
return (T)Application.Current.Resources[key];
}
and it finds the relevant DataTemplate and displays it accordingly in my pivot control based on the relevant PivotItem ViewModel.
Now, I'm not out of the woods yet as I have no idea if binding to the relevant viewmodel will work but that's a whole other story!
If anyone knows why DataTemplate cannot be found when defined in Pages.Resources or Grid.Resources, please update the post as I'd love to know why.
Thanks.
I am doing TextBox binding as shown below. But the background color doesn't change. Any help is appreciated!
<TextBox x:Name="FirstNameTextbox" Text="Test" Background="{Binding Path=FirstNameBackground,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
C# code:
public SolidColorBrush FirstNameBackground
{
get
{
return firstNameBackground;
}
set
{
firstNameBackground = value;
OnPropertyChanged("FirstNameBackground");
}
}
A couple things.
A). Have you implemented INotifyPropertyChanged, without it binding to the xaml will only work one way and then stop, since there is no way to notify when property has changed.
B). Your xaml is not properly built. You must have a closing bracket at the end of textbox such as
<textbox/>
or
<textbox></textbox>
C). How is your datacontext set? If it is not set to anything , this will not work. This can be done by
a. datacontext =this in your codebehind
b. settings datacontext in xaml using window.datacontext as the xaml key
D). The information given is very very vague and i can only make decisions and suggestions based on common mistakes I have seen when building xaml/wpf apps. Please provide more information on
1. How datacontext is set.
2. How full xaml looks like
3. full codebeind/viewmodel if applicable
I have a boolean property in my ViewModel, named lets say IsNotSupported that is used to show some warning information if a sensor is not supported. Therefore I use a BooleanToVisibilityConverter, that is added in the ressources:
<phone:PhoneApplicationPage.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</phone:PhoneApplicationPage.Resources>
and bind it to the stackpanel containing the warning:
<StackPanel x:Name="NotSupportedWarning" Visibility="{Binding IsNotSupported,
Converter={StaticResource BooleanToVisibilityConverter}}">
That works all quite well, but when loading the page, and the sensor is supported, the warning appears for just a fraction of a second and disappears afterwards. I know that this flickering is caused by the binding not having happened yet and therefore defaulting to visible.
That flicker it is annoying as hell... It should rather default to collapsed and be made visible only after it is clear that the warning should be shown. Also, this would avoid a second layouting pass after the binding and could therefore have positive performance impacts.
I had this problem over and over, and found nothing about it in the internet until I found this SO question, that is closely related, but is not found if searched for windows phone instead of silverlight. Both the problem and the solution might seem simple, but I really bugged me quite a long time, so I thought it might be a good idea to write a Q&A-style question about it to help others that are facing the same issue.
The solution is simple after you have seen it. You can control the default value of the binding (if the binding didnt happen yet) with FallbackValue. Your stackpanel XAML would look like:
<StackPanel x:Name="NotSupportedWarning" Visibility="{Binding IsNotSupported,
FallbackValue=Collapsed,
Converter={StaticResource BooleanToVisibilityConverter}}">
This way you get rid of the flicker and it does not have to be relayouted after the binding, if the warning stays hidden.
you can bind directly to a Visibility type of property instead of boolean and keep that property to collapsed by default plus you can implement INotifyPropertyChanged
FINAL NOTE
Final solution found in another post
Although I appreciated the clarification that was provided, the ultimate solution was in-fact provided by another solution as linked above. No matter WHAT I tried, the binding via the "Element Name" component was not working. I had to go based on the "Relative" hierarchy up the datagrid...
<Button Name="btnPrintReport"
Command="{Binding DataContext.MyPrintCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Height="16" Width="16" HorizontalAlignment="Center" >
<Image Source="MyButtonImage.png" IsHitTestVisible="True"/>
</Button>
Hope something not too complicated in WPF / MVVM environment. Here's the scenario.
I have a Window (.xaml) and a corresponding View Model (.cs). The form displays fine with all the data bindings no problem. (note: this is NOT done via any commercial "framework" )
One of the controls that is in the view window is a custom user control of a datagrid with all pre-defined columns, headings and content to be displayed when the view is shown. This works all no problem even though the control is not directly "defined" in the main window .xaml file, but just dropped on the form as the user control itself (which has its own obvious .cs code-behind).
With the main window's "DataContext" pointing to the View Model, and the user control that has a datagrid
<DataGrid AutoGenerateColumns="False"
Name="dataMyStuff"
ItemsSource="{Binding Path=MyTablePropertyOnViewModel,
NotifyOnSourceUpdated=True,
NotifyOnTargetUpdated=True}" ... />
Now, what I'm looking for. On this data grid, I have a column that has an image in the first column. When I click on this image, I want to print a report specific to the record as represented by this row (it has a PK value I use). So, how do I tell the image "KeyUp" event to go to the View Model event handler since that is where the data is, and some other methods I'll need for preparing the call to the report. The view portion of the grid is for cosmetic display to the user, and thus no "functionality" directly in this control.
-- EDIT -- per progress from answers
I've adjusted my datagrid per comments from Josh and Rachel, however, something still does not appear to be quite right... Seeing the button was using a "Command" instance, I interpreted this as it needed to attach to an instance of an "ICommand" interface object on my view model. So, I created an instance. I know the command handler works as it is also used for common things like Add, Edit, Save, Cancel, Exit, etc... So I have a new one for this printing purpose. For simplicity, I have it created as an Always Execute, so there is no method to handle the "CanExecute" portion of the control. I've set the button's "Command" to almost all iterations I could think of an still nothing, but here's an update of what I'm seeing going on.
<UserControl>
<Data grid columns / template, etc to the button>
<DataTemplate>
<Button Name="btnPrintReport"
Command="{Binding DataContext.MyPrintCommand}" >
<Image Source="myPrintImage.png"/>
</Button>
</DataTemplate>
</Data grid columns, etc>
</UserControl>
In my ViewModel class (myICommandButtonHandler inherits from ICommand)
private myICommandButtonHandler myPrintCommand;
public myICommandButtonHandler MyPrintCommand
{
get { if (myPrintCommand == null)
myPrintCommand = new myICommandButtonHandler(myPrint);
return myPrintCommand;
}
}
private void myPrint()
{
MessageBox.Show( "Doing the print job..." );
}
Now, what I'm seeing. During step through initialization of all the controls and such. I click menu item to call my Window to be displayed. FIRST, it creates an instance of the View Model controller. THEN, it calls the Window and passes in the View Model controller as parameter so it is immediately set at the Window level as the "DataContext" of the window. The main window then goes into it's "InitializeComponents" call and starts to build out all the other controls embedded, including this separate class that contains the data grid in question. At the constructor of this usercontrol (that has the datagrid), there is no "data context" set yet as the rest of the controls have not been initialized yet, and I don't know why / when the "bindings" apparently get "hooked" in. So, it appears that trying to do the binding to the data grid's command button are failing. HOWEVER, at run-time, the actual data IS updated in the grid, so I know that much is working.
So, the data grid has its "ItemsSource" set to a property of a "DataView" property on the view model, but the binding of the "button" doesn't appear to see the "MyPrintCommand" handler that I thought would get the hit.. and its action is to just display a message box (for now).
Usually I use an AttachedCommand Behavior which allows me to bind Events to ViewModel Commands. For example, you could use
<Image ...
local:CommandBehavior.Event="KeyUp"
local:CommandBehavior.Command="{Binding DataContext.PrintCommand, ElementName=dataMyStuff}"
local:CommandBehavior.CommandParameter="{Binding }"/>
I'd would recommend using a different event than KeyUp, since I don't think Images can have Keyboard focus so the KeyUp event will never get fired.
A better alternative is to use a Button and overwrite it's Template to be your Image. This will maintain the Click functionality, and give you access to Command and CommandParameter properties
<Button Command="{Binding DataContext.PrintCommand, ElementName=dataMyStuff}"
CommandParameter="{Binding }">
<Button.Template>
<Image ... />
</Button.Template>
</Button>
Also, the CommandParameter="{Binding }" will simply pass the current DataRow's DataContext (your data object) to the command
Change the data template to be a button that has a image as its content. Use the command and commandparameter properties on the button to call your printing method. You can declare your print command in your viewmodel, and bind to it. Your parameter could be the selected row in your datagrid.
After wasting hours on this, following on the heels of my Last Problem, I'm starting to feel that Framework 4 is a master of subtle evil, or my PC is haunted.
I have three comboboxes and a textbox on a WPF form, and I have an out-of-the-box Subsonic 3 ActiveRecord DAL.
When I load this "edit record" form, the comboboxes fill correctly, they select the correct items, and the textbox has the correct text. I can change the TextBox text and save the record just fine, but the comboboxes CANNOT BE CHANGED. The lists drop down and highlight, but when you click on an item, the item selected stays the same.
Here's my XAML:
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Asset</TextBlock>
<ComboBox Name="cboAsset" Width="180"
DisplayMemberPath="AssetName"
SelectedValuePath="AssetID"
SelectedValue="{Binding AssetID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Status</TextBlock>
<ComboBox Name="cboStatus" Width="180"
DisplayMemberPath="JobStatusDesc" SelectedValuePath="JobStatusID"
SelectedValue="{Binding JobStatusID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Category</TextBlock>
<ComboBox Name="cboCategories" Width="180"
DisplayMemberPath="CategoryName"
SelectedValuePath="JobCategoryID"
SelectedValue="{Binding JobCategoryID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Reason</TextBlock>
<TextBox Name="txtReason" Width="380" Text="{Binding Reason}"/>
</StackPanel>
Here are the relevant snips of my code (intJobID is passed in):
SvcMgrDAL.Job oJob;
IQueryable<SvcMgrDAL.JobCategory> oCategories = SvcMgrDAL.JobCategory.All().OrderBy(x => x.CategoryName);
IQueryable<SvcMgrDAL.Asset> oAssets = SvcMgrDAL.Asset.All().OrderBy(x => x.AssetName);
IQueryable<SvcMgrDAL.JobStatus> oStatus = SvcMgrDAL.JobStatus.All();
cboCategories.ItemsSource = oCategories;
cboStatus.ItemsSource = oStatus;
cboAsset.ItemsSource = oAssets;
this.JobID = intJobID;
oJob = SvcMgrDAL.Job.SingleOrDefault(x => x.JobID == intJobID);
this.DataContext = oJob;
Things I've tried:
Explicitly setting IsReadOnly="false" and IsSynchronizedWithCurrentItem="True"
Changing the combobox ItemSources from IQueryables to Lists.
Building my own Job object (plain vanilla entity class using INotifyPropertyChanged).
Every binding mode for the comboboxes.
ItemsSource="{Binding}"
The Subsonic DAL doesn't implement INotifyPropertyChanged, but I don't see as it'd need to for simple binding like this. I just want to be able to pick something from the dropdown and save it.
Comparing it with my last problem (link at the top of this message), I seem to have something really wierd with data sources going on. Maybe it's a Subsonic thing?
EDIT: For some reason the set accessor is hit only on the AssetID property and only the first time. WPF is now heading for WTF :)
EDIT 2: You gotta be kidding me- I've removed the binding (ie it only has a displaymemberpath, a valuememberpath and an itemssouce) and it's STILL doing it! It accepts your first selection, and then won't change.
WPF Combo Boxes will not change the selected item if the currently selected item and the item that was just selected are considered equal by the object.Equals() method called on the newly selected object (i.e newlyslected.Equals(previoslySelected) ).
Overriding the Equals method on the class your binding the combobox items, should resolve the issue your are seeing.
I've narrowed it down to the Subsonic objects used as ComboBoxItems.
If you create a new class that uses exactly the same code as the relevant parts of the Subsonic one, it works.
If you use POCOs/datatables for the combos and Subsonic for the record being edited, it works.
But if you use Subsonic for both, it doesn't.
I had hoped to extend the subsonic objects and not have to code a full-blown BLL tier. Looks like I'm faced with doing that or throwing out Subsonic for the DAL. I might post a more specific question for the Subsonic folks.
Many thanks to all who contributed.
Old topic but I had the same problem and difficulty finding solution. This might help someone else.
Clue is above in WPF not detecting a different item has been seleted by user. (Symptom - event ComboBox_SelectionChanged only fires on first selection)
My scenario - lookup combo populated from IList built from a DISTINCT query. In this case the result of using NHibernate ICriteria.SetResultTransformer which only returns SOME fields, importantly NOT including the unique entity ID.
Solution - loop thru' IList after retrieval and give each entity a unique ID. WPF sees them as individuals and behaves appropriately.
Its only a value lookup - its the value content I was after.
The 'temporary' entities are never persisted. In this case it was a better approach than messing with overriding the object's Equals method for the sake of a simple GUI issue. An alternative would be to just copy or tranform the list into a format where WPF uses the value field to determine 'difference'...
Sounds like the field is somehow readonly, or that your change isn't being persisted. After the binding sets the new value, it will re-read the property to ensure that it was actually changed. If your property returns the old value, then it'll be re-selected in the combo box, giving the appearance that the value never changed.
I don't know that DAL, but can you step through the property setter code? You might also have an issue with type conversion.
EDIT reading your comment about the red rectangle -- it sounds as though your property (or something to do with the binding) is raising an exception. Unless, of course, you're using data validation in your UI. You might turn 'Break on all exceptions' in the debugger's settings, assuming you're using Visual Studio.
EDIT 2 You should check the VS Output pane for any error messages related to binding. You can also read this blog post which gives more info on debugging bindings.
It's hard to tell from a small sample of your code but try commenting out the line:
//this.DataContext = oJob;
and see if this helps.
Setting the DataContext and ItemsSource might be causing a conflict.
Did you write any global style for your combo box which may have a bug or something missing? Or are you using pure default styles for your combobox? Try removing any default styles applied.
Are you wiring up any events? If your code hooks up for event like PreviewMouseLeftButtonUp and marks event as handled then probably combobox may ignore and wont select anything.