How to make button bound to ICommand enable/disable when text changes, as opposed to losing focus of TextBox - xaml

I have a Windows Store XAML app with a "Save" button, which points to an ICommand property in my ViewModel.
<Button Content="Save"
Command="{Binding DataContext.SaveNewNote, ElementName=grid, Mode=OneWay}"
CommandParameter="{Binding DataContext.CurrentItem, ElementName=grid}" />
on the propertychanged event of my Note model, that fires the CanExecutedChanged event - every time (every keystroke). However, this button will only enable/disable once I leave focus of one of the related textboxes.
In other words, there is a "Name" textbox. If String.IsNullOrWhiteSpace(Name.Text), then CanExecute is set to false. This seems to execute with every keystroke.
I would expect to see this bound Save button enable and disable with each keystroke. HOWEVER, instead, I see the button enable/disable only when I tab out of the textbox.
How can I get this bound button to respect the CanExecuteChanged event on every keystroke, instead of when the texbox loses focus?

The comments by #will and #patrick on the original post both had the correct answer. To do this, you must set the "UpdateSourceTrigger" to "PropertyChanged" and that gives the intended results.
<TextBox Grid.Row="1" PlaceholderText="Enter a short description here"
Text="{Binding NoteAbstract, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox>

Related

how to disable auto tab after pressing enter and losing focus of textbox

I have an Event Handler for a keyDownEvent in a TextBox. Now when I press Enter this Event Handler changes the Visibility to Collapsed and the TextBox hides. the Problem with it is that this obviously unfocus the TextBox and foucus sth. else in my App. How can I disable this autofocus of another element and focus nothing?
I have tried the following but i am really screwed.
if (e->Key == Windows::System::VirtualKey::Enter) {
this->mode = ITEM_MODE::SELECT; // will Change Visibility to Collapsed
FocusManager::TryMoveFocus(FocusNavigationDirection::None);
e->Handled = true;
}
Thanks for your help! <3
how to disable auto tab after pressing enter and losing focus of textbox
Derive from official document,
UseSystemFocusVisuals used to get or set a value that indicates whether the control uses focus visuals that are drawn by the system or those defined in the control template.
You could set control UseSystemFocusVisuals property as False. Then next control will not be focused by system.
<StackPanel Orientation="Vertical" Name="RootLayout" IsTapEnabled="False" >
<TextBox HorizontalAlignment="Stretch" Height="44" KeyDown="TextBox_KeyDown" />
<Button Content="FouceElement" UseSystemFocusVisuals="False"/>
<Button Content="NextBtn" UseSystemFocusVisuals="False"/>
</StackPanel>
In case you just don't want the auto-focused item to have focus indicator shown, you can change the Focus type to FocusState.Pointer.
Example (C#):
object focusedElement = FocusManager.GetFocusedElement();
((Control)focusedElement).Focus(FocusState.Pointer); // This will hide focus indicator

wp10:TextBox with flyout not firing lost focus on every got focus

Existing OS version :WinowsPhone 8.1.
Device: Nokia Lumia 925.
XAML code:
<TextBox
x:Name="txtDtpEnd0"
Grid.Row="2"
Grid.Column="0"
Width="130"
Height="30"
Margin="22,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
BorderBrush="Gray"
BorderThickness="0.5"
FontFamily="Segoe UI"
FontSize="15"
Foreground="Black"
GotFocus="DeclarativeInlineButton_GotFocus">
<FlyoutBase.AttachedFlyout>
<DatePickerFlyout
x:Name="DtpEnd0"
Title="Select a date"
Closed="datePicker_Closed"
DatePicked="datePicker0_DatePicked" />
</FlyoutBase.AttachedFlyout>
</TextBox>
Code in c# :
private void DeclarativeInlineButton_GotFocus(object sender, RoutedEventArgs e)
{
Windows.UI.ViewManagement.InputPane.GetForCurrentView().TryHide();
TextBox TB = (TextBox)sender;
FlyoutBase.ShowAttachedFlyout(TB);
//Windows.UI.ViewManagement.InputPane.GetForCurrentView().TryShow();
}
Functionality in WP8.1
On focusing of this text box datepicker flyout appear an user can select a date from date picker.
Result In WP8.1
User able to select date and functionality work as expected.
Result In WP10
On focus of textbox softkeyboard appear but sometimes datepicker also appear.
easy Case for soft keyboard : when i hold back button for long and resume to app this and try to focus textbox soft keyboard start appearing. to remove softkeyboard i need to close app and relaunch it.
Conclusion:
But sometimes softkeyboard start appearing without back button hold.
Finally after applying latest windows 10 update(10.0.14393.693) this problem resolved without making any changes in app.

Xamarin Forms - Multiple DataTemplate ListView

From my previous experience and some research on the web, I don't know if it's possible to have the following behavior:
<ContentPage.Content>
<AbsoluteLayout BackgroundColor="#2F767B">
<AbsoluteLayout x:Name="ScreenLayoutAdapter" BackgroundColor="#235A5E"
AbsoluteLayout.LayoutFlags="All">
<AbsoluteLayout.LayoutBounds>
<OnPlatform x:TypeArguments="Rectangle" Android="0, 0, 1, 1" iOS="1, 1, 1, 0.975" WinPhone="0, 1, 1, 1"/>
</AbsoluteLayout.LayoutBounds>
<ListView x:Name="ListViewCourses" BackgroundColor="Transparent" RowHeight="90"
AbsoluteLayout.LayoutBounds="0.5,1,0.9,0.9"
AbsoluteLayout.LayoutFlags="All">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<AbsoluteLayout Margin="2">
<!-- Your design for the cell template -->
</AbsoluteLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
</AbsoluteLayout>
</ContentPage.Content>
In the c# side, I then have a ObservableCollection<Item> that I bind with the ListViewCourses.
But now my question is:
When I touch an item, how can I change of DataTemplate cell?
I mean, it's a list of items but when I touch one, I want that the cell grows up and displays more information, about the item selected.
If I have to bring more information to make you understand, tell me
Thank in advance ! :)
What you could do, instead of changing the DataTemplate of a single item, is have all of the controls containing the extra information that you want to show, be set to IsVisible = false and then when it is clicked, set IsVisible = true on all the controls and also call ListView.ForceLayout() to force the ViewCell to get redrawn.
Finally, make sure ListView.HasUnevenRows = true or else the ViewCell will not change size.
Alternatively, you may be able to add the controls containing extra information to the ViewCell when it is selected, it may or may not be slower/faster depending on the number of items in the ListView, the amount of extra controls you are adding, and whether you need to query the DB or a service for that extra information.
Let me know if you have any questions/issues.
Edit: Actually, you will want to call ViewCell.ForceUpdateSize(), info here, in order to change the size of the ViewCell once selected. You also may need to store that ViewCell instance in a class level variable so that you can shrink it back down when the user clicks a different ViewCell in the list.
Edit #2: One last thing I have run into, is that on iOS, if you have enabled ListViewCachingStrategy.RecycleElement on your ListView, you will most likely not see any change in the ViewCell after making controls visible and calling ForceUpdateSize(). This might have something to do with the last paragraph here, but I am not sure how to properly fix/get around it at this time.
Edit #3: For example you might do something like this:
<ViewCell>
<!-- Use ListView.ItemTapped instead of the TapGestureRecognizer below -->
<!-- <ViewCell.GestureRecognizers>
<TapGestureRecognizer Tapped="OnViewCellTapped"/>
</ViewCell.GestureRecognizers> -->
<StackLayout>
<!-- Main info displayed by default -->
<StackLayout StyleId="DefaultStackLayout">
<Label Text="{Binding ItemName}"/>
<Label Text="{Binding ItemDates}"/>
<Label Text="{Binding ItemCredits}"/>
</StackLayout>
<!-- Extra info displayed upon selection -->
<StackLayout StyleId="ExtraStackLayout"
IsVisible="False">
<Label Text="{Binding ItemBuilding}"/>
<Label Text="{Binding ItemTeacher}"/>
<Label Text="{Binding ItemHours}"/>
</StackLayout>
</StackLayout>
</ViewCell>
Then when the user selects the cell, you would need to do something like this:
using System.Linq;
...
private void OnViewCellTapped(object sender, EventArgs args) {}
tapCount++;
ViewCell viewCellSender = (ViewCell)sender;
StackLayout rootStack = (StackLayout)viewCellSender.View;
StackLayout extraInfoStack = rootStack.Children.Where(stack => stack.StyleId == "ExtraStackLayout");
extraInfoStack.IsVisible = true;
viewCellSender.ForceUpdateSize();
}
I have not tested any of this or even attempted it before, so if the above does not work, I am confident that inserting the extraInfoStack element content once the ViewCell is clicked, will work. But give the above a try first. Obviously you will need to change the layouts if you want to use AbsoluteLayout and you will also need to change the OnViewCellTapped() casting code.
Edit #4: I usually try to steer clear of constant numbers for height and width values if possible but sometimes it is unavoidable. So for this all to work you are going to have to set ListView.HasUnevenRows = true and that means you will need to get rid of your ListView.RowHeight value completely since the rows need to be allowed to have variable height.
If at all possible, I would try to not set a constants height value, but if you absolutely cannot live without setting the height to a constants value, then you can probably give your ViewCell's inner AbsoluteLayout a HeightRequest of 120, but then you would need to change that value when the item is selected, after making your extra controls visible and before calling ViewCell.ForceUpdateSize(). Setting IsVisible to false is supposed to make it so that the control does not take up any space, so those extra controls should not mess with the sizing while they are hidden but I have never tried it myself.
If you run into issues, please let me know.

Using x:Bind inside the GridView's ItemTemplate layout User Control in UWP

In the Universal Windows Platform API, how do I use x:Bind inside of a User Control (intended to be the layout for a GridView's ItemTemplate) to bind to instance properties of a GridView's ItemSource?
Background
I'm trying to re-create the layout found in Windows 10 stock apps like Sports, News, Money, etc.
I'm using a two GridViews for the main area of the app; one for "featured articles" (2 large photos w/ headlines) and one for all the other articles (smaller photos w/ headlines).
I'm able to bind to a data source that I supply in the code behind (a List where NewsItem is a POCO with a Image and Headline property) Here's the pertinent parts of the MainPage.xaml:
<Page ...
xmlns:data="using:NewsApp.Models" />
....
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
....
The Image and Headline bind just fine (even though they've not been styled correctly). However, instead I think I need to bind to a User Control to get the styling options I want, control over resizing esp. when using Visual State Triggers and to simplify the XAML in general (at least, this was the technique suggested to me.)
So, I added a new User Control to the project (FeaturedItemControl.xaml), and copied in the DataTemplate's child Grid:
<UserControl ... >
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</UserControl>
And then back in the MainPage.xaml, I change the DataTemplate to reference the new FeaturedItemControl:
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<local:FeaturedItemControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
However, I get the error message for both Image and Headline properties: Invalid binding path 'Headline': Property 'Headline' can't be found on type 'FeaturedItemControl'.
I've tried a few things but am flailing just throwing code at the problem without understanding what I'm doing. Any help would be greatly appreciated.
Thank you for your kind attention.
Using Depechie's answer, I formulated this little cheat cheat for posterity:
Do note that you MUST use this technique to utilize the VisualStateManager with items inside your data bound controls' (GridView, ListView) data templates.
1) Create a User Control.
2) Cut the content of the DataTemplate in your page and paste it into the User Control replacing the template's Grid.
3) Reference the User Control from inside the Data Template:
4) Modify the contents of the User Control changing x:Bind statements to utilize object.property notation:
<UserControl>
<StackPanel>
<Image Source="{x:Bind NewsItem.LeadPhoto}" />
<TextBlock Text="{x:Bind NewsItem.Headline}" />
<TextBlock Text="{x:Bind NewsItem.Subhead}" />
</StackPanel>
</UserControl>
5) Add this in the User Control's Code Behind:
public Models.NewsItem NewsItem { get { return this.DataContext as Models.NewsItem; } }
public ContactTemplate()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => Bindings.Update();
}
Well it's possible to use x:Bind in user controls, but you'll need to add some extra code behind.
I encountered the same problem in my project, you can see the result here : https://github.com/AppCreativity/Kliva/tree/master/src/Kliva/Controls
So what you need to do is, create a property in the code behind of your user control that points to the correct DataContext.
If you do that, you can use properties of that DataContext in the xaml of your control: for example:
Do note that in the constructor of your control you do need to add: DataContextChanged += (sender, args) => this.Bindings.Update(); because the datacontext will change depending on the page where your control is used!
Then on the page where you are placing this control, you'll also need to do the same to enable the x:bind to work.
You'll see this in my example on the MainPage.DeviceFamily-Mobile.xaml and MainPage.xaml.cs files.
Hope this helps.
x:Bind isn't really hierarchical like Binding/DataContext is. Additionally when you're not directly inside a DataTemplate (such as inside your user control) the object that x:Bind tries to use is 'this' rather than 'this.DataContext'. My current line of thinking on how to solve this sort of issue is to try not to use UserControls anywhere. Instead preferring DataTemplates contained within a ResourceDictionary. There are some pretty strong caveats to this approach though, you will for example crash the xaml compiler if you use x:Bind inside a data template that was created from the ResourceDictionary item template (add new item). you can find a pretty complete example here https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlBind its important to note in the sample where they show the ResourceDictionary being used that its not actually just a ResourceDictionary.xaml its also a ResourceDictionary.xaml.cs (this is where the generated code from x:Bind ends up)
Another option is to add Headline and Image as properties on your user control and x:Bind them from the template, then inside the user control x:Bind as you are currently doing, but now the x:Bind generated path 'this.Headline' will exist. Unfortunately the order things are actually bound means that the x:Bind's you have inside your user control will have to be OneWay rather than the default OneTime. this is because x:Bind OneTime does the bind inside the InitializeComponent call, and any set of properties/DataContext stuff doesn't get done until after that has already run.
So to sum this up, you have two options, use data templates everywhere, or bind to properties that are directly on the user control.

What prevents a Navigation between two XAML-pages?

I am experiencing a very strange bug in my c++/cx XAML app:
I have a back-button that checks if the progress you made is saved and (in case it isn't) pops up a flyout that lets you save or leave without saving. This is done with this->frame->goBack() in both cases, however:
When the progress was saved, the app halts at a __debugbreak() however, when goBack() is called by the button on the flyout, everything works out fine. Why could that possibly be the case?
Things that might help you:
The app is based on the "Blank App" template, the pages itself are based on the "Basic Page" template provided by Visual Studio 2013
The Controls in BoardPage.xaml are defined as follows:
<AppBarButton x:Name="backButton" Icon="Back" Height="95"
Click="backButton_Clicked">
<AppBarButton.Resources>
<Flyout x:Key="WarningFlyoutBase">
<Grid Height="150" Width="200">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Text="Unsaved progress, what do you want to do?"
HorizontalAlignment="Center" TextWrapping="Wrap"
FontSize="14" Margin="4,10" TextAlignment="Center"/>
<Button x:Name="WarningSaveButton"
Content="Save now."
Grid.Row="1" HorizontalAlignment="Center"
Click="WarningSaveButton_Clicked"/>
<Button x:Name="WarningLeaveButton"
Content="Leave without saving."
Grid.Row="2" HorizontalAlignment="Center"
Click="WarningLeaveButton_Clicked"/>
</Grid>
</Flyout>
</AppBarButton.Resources>
<AppBarButton.Flyout>
<StaticResource ResourceKey="WarningFlyoutBase"/>
</AppBarButton.Flyout>
</AppBarButton>
so these three controls (backButton, WarningSaveButton and WarningLeaveButton) all have their respective Clicked event handlers, though only two of them are relevant right now:
backButton:
void Tackle::BoardPage::backButton_Clicked(Platform::Object^ sender,
Windows::UI::Xaml::RoutedEventArgs^ e)
{
if (saved && Frame->CanGoBack) /* saved is a bool */
this->Frame->GoBack();
else
backButton->Flyout->ShowAt((FrameworkElement^)sender);
}
Note: I also tried replacing GoBack() with Navigate(TypeName(CreateGamePage::typeid)), but that didn't help.
WarningLeaveButton:
void Tackle::BoardPage::WarningLeaveButton_Clicked(Platform::Object^ sender,
Windows::UI::Xaml::RoutedEventArgs^ e)
{
if (Frame->CanGoBack)
this->Frame->GoBack();
}
other strange stuff:
I tried to examine the reasons for this crash/debugbreak for quite some time, and found the following:
the page in question is navigated to with this->Frame->Navigate(TypeName(BoardPage::typeid), ref new CGameSession(job));. leaving out the second argument fixes the crash magically.
When the breakpoint is triggered in App.g.hpp, opening a watch on the value errorMessage reveales:
"Placement target needs to be in visual tree."
How come the Flyout is in the visual tree, but the Button it's been attached to isn't?
The target page is in fact constructed, but the NavigationHelper->OnNavigatedTo(e) method fails in the last line LoadState(this, ref new LoadStateEventArgs(e->Parameter, safe_cast<IMap<String^, Object^>^>(frameState->Lookup(_pageKey))));, wich seems paritularly odd, because LoadState() gets called easily and only contains two (void) typecasts. (I have not modified a single one of these methods.)
The Problem here lies within the way flyouts and event handlers work with XAML-Controls, there are 2 mayor points that cause my code to crash:
Flyouts are always shown when the button they're attached to is clicked or tapped.
Event Handlers seem to be executed before the flyout gets shown.
What follows from this?
Well, when the backButton is pressed, backButton_Clicked gets triggered first.
When the game is not saved, the flyout opens and you can go back without a Problem.
But if the game was saved previously, the event handler immediately calls this->Frame->GoBack(), wich does exactly what it should, constructs the previous page, loads its state and draws it.
BUT after that, it tries to open the flyout, and this is the problem here: The flyout doesn't exist anymore, hence the strange error Message.
I am kind of embarassed it took me this long to figure it out, but maybe someone will find this useful in the future, so I'm going to leave this question here.
Workaround:
The workaround I'm using for this now is to move the FlyoutBase to the <Page.Resources> and attach it to a button with a size of 0x0 px. Triggering the flyout then done like so: Flyout::ShowAttachedFlyout(InvisibleButton);