I'm using the TextBoxValidationExtension in a MVVM pattern. I was having a problem with the validation because I'm setting my binding source in TwoWay mode in the NavigatedTo method which is called after the TextBoxFormatValidationHandler.Attach method is called. The first validation therefore occurred with empty value on the textbox which was applying the error styling to the textbox.
The binding in the NavigatedTo to the Text property of the textbox wasn't triggering a Textbox TextChanged event since from my comprehension the Textbox control is not loaded at this point.
So even tough I had a valid value binded to the textbox it appears to be invalid since the extensions didn't validated it.
<TextBox Text="{Binding Path=ObjectXYZ.PropertyABC, Mode=TwoWay}"
extensions:TextBoxFocusExtensions.AutoSelectOnFocus="True"
extensions:FieldValidationExtensions.Format="NonEmpty,Numeric">
What I've done to solve the problem was to add to the WinRT Toolkit TextBoxFormatValidationHandler a handler to the loaded event of the textbox in the TextBoxFormatValidationHandler.Attach method:
internal void Attach(TextBox textBox)
{
if (_textBox == textBox)
{
return;
}
if (_textBox != null)
{
this.Detach();
}
_textBox = textBox;
_textBox.TextChanged += OnTextBoxTextChanged;
_textBox.Loaded += _textBox_Loaded;
this. Validate();
}
void _textBox_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.Validate();
}
If someone has a better solution can you please let me know, thanks!
Related
I am using Prism and Autofac with Xamarin.Forms 4.0 with an MVVM architecture. Using the Navigation.NavigateAsync("MyPage") works unless I have a binding to the Date object with my ViewModel.
The page renders properly and I am navigated to it if my DatePicker has no binding.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" />
However the following will cause me to never navigate to the page.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" Date="{Binding SelectedStartDate, Mode=TwoWay}"
The property in the View Model, MyVM, looks like this.
private DateTime selectedStartDate;
public DateTime SelectedStartDate
{
get
{
return selectedStartDate;
}
set
{
SetProperty(ref selectedStartDate, value);
sample.ProcessStartDate = value;
}
}
Navigation with the following code fails with the Binding in XAML above:
INavigationResult status;
try
{
var parameters = new NavigationParameters();
parameters.Add("CurrentSample", SelectedSample);
status = await NavigationService.NavigateAsync("MyPage", parameters); //MyPage is registered with MyVM
}
catch (Exception ex)
{
string mess = ex.Message;
}
My work-around is to add an event handler to the code-behind.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" DateSelected="OnStartDateSelected"
So now my code-behind has a handler:
void OnStartDateSelected(object sender, DateChangedEventArgs args)
{
SampleDetailsViewModel vm = BindingContext as SampleDetailsViewModel;
vm.SelectedStartDate = args.NewDate;
}
I have a work-around for this page, But I don't want put code in the code-behind. This breaks the MVVM standard that I've managed to maintain on the other seven pages of the app. Am I Binding improperly with the DatePicker?
When Binding SelectedStartDate, you are not initializing it, making it binding to a null, because you have set the Binding Mode to "TwoWay".
Here you can find the various types of binding modes, quoting:
Causes changes to either the source property or the target property to
automatically update the other. This type of binding is appropriate
for editable forms or other fully-interactive UI scenarios.
a solution would be something like this (if you wanna keep the TwoWay mode, and dont mind starting with an default selected):
private DateTime selectedStartDate = DateTime.Now;
Or
Making the binding mode to "OneWayToSource", this makes updates to the binding source without, and not the target (remember that this way you can't change the selected date from the binding, only the datepicker can).
Updates the source property when the target property changes.
Or
If you wanna keep the TwoWay Mode and not having a default date selected, the way you did with code behind is a nice workaround.
I have a really odd problem with variable scopes. A Listview named "TodoListView" is defined via xaml, and it's ItemSource populated from a SQListe database. Works. Inside the ListView I have a ViewCell to display the data row-wise.
<ContentPage ... x:Class="JanitorPro.MainPage" ...>
<StackLayout>
<ListView x:Name="TodoListView" Margin="20" ItemSelected="OnListItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="{Binding name}" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" />
<Switch HorizontalOptions="End" IsToggled="{Binding done}" Toggled="DoneSwitchToggled"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
The codebehind looks like this (some irrelevant portions removed):
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
base.OnAppearing();
// load Database
TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
}
async void OnReloadButtonClicked(object sender, EventArgs e)
{
Debug.WriteLine("Reload Button Click");
TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
Debug.WriteLine("Reload done.");
}
async void OnListItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem != null)
{
await Navigation.PushAsync(new TodoItemPage
{
BindingContext = e.SelectedItem as TodoItem
});
}
}
private void DoneSwitchToggled(object sender, ToggledEventArgs e)
{
// TodoItem i = null;
TodoItem i = TodoListView.SelectedItem;
if (i != null)
{
Debug.WriteLine("Toggle: {0}", i.id);
}
}
}
}
The oddity has two stages. Before I inserted the DoneSwitchToggled event handler, every occurrance of TodoListView.ItemsSource got a red underline under TodoListView and a hint that "The name TodoListView does not exist in the current context". OK, I thought that VS was not smart enough to find a definition in the xaml file, because, despite of the warning, the program compiled and ran fine. TodoListView gets initialized and does correctly display the rows of the underlying database, so it does clearly exist at runtime.
Things went wild when I added the DoneSwitchToggled event handler to both XAML and the codebehind. All the sudden the program won't compile any longer but bail out with a CS0103 error "The name "TodoListView" does not exist in the current context". The error appears three times, with the line numbers pointing to the other occurrances of TodoListView in onAppearing() and OnReloadButtonClicked(). Huh? How the heck can the addition of a variable reference in an event handler render that variable invalid in completely different methods? OK, there was something fishy with the variable before (still don't know what ...), but it worked. Now it doesn't any more, whch doesn't make any sense for me. Furthermore, if I comment out the offending line in the DoneSwitchToggled event handler, and insert a dummy definition for i, like so:
TodoItem i = null;
// TodoItem i = TodoListView.SelectedItem;
everything is like before, VS still underlines the other appearances of TodoListView, but now the program builds and runs ok again.
Anyone who can explain this effect, and show me how correct my code? I think the objective is clear: DoneSwitchToggled is supposed to write back the switch value into the database (and do some other processing not shown in my stripped down sample), and though the "sender" object is correctly set to reference my button, I found no way to access the underlying data binding, since ToggledEventArgs unfortunately does seem to only pass the switch position "true" or "false", but - unlike the OnListItemSelected event handler - not pass any reference to the bound row through the second argument. So my idea was to use ListView.SelectedItem for this purpose.
Finally I figured it out myself. This seems to be a glitch in VS 2017. There is nothing wrong with TodoListView, so error CS0103 is misleading nonsense.
What VS really means is an error CS0266. TodoListView is defined by a generic list
List<TodoItem>
to access SelectedItem i need to typecast it:
TodoItem i = (TodoItem)TodoListView.SelectedItem;
This added, all errors are gone, app code builds OK.
Btw, unfortunately this approach to get at the item where the Switch has been flipped has proven not working, TodoListView does always return null for SelectedItem, seems that the ListView control doesn't see the button press. Need to find a different way to find the list item beneath the switch to get at the bound row id.
How to automatically raise event in windows phone? For example, I have an element <Image name = "image" .... />. I want when a MainPage is loaded, it will automatically raise tap event on that element
If you want to declare tap event dynamically (loads tap event on page load), You can declare it in following way. Here is your xaml.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image Name="image1"/>
</Grid>
And in constructor,
public MainPage()
{
InitializeComponent();
image1.Tap += image1_Tap;
}
void image1_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//Perform your action here
//This method invokes only when you tap on image
}
Else, try the other way.
Loaded += (s, e) =>
{
//Actions that are performed when image is tapped
}
Add above lines in your constructor.
This probably is not a good solution to whatever you are trying to accomplish. It can be done with reflection - How to manually invoke an event? However I'd just extract your Tap event code to method and then call this method in your Loaded event and also Tap even.
View:
TextBox x:Name="feedback" Text="{Binding FeedbackText,Mode=TwoWay}"
ViewModel:
public string FeedbackText
{
get
{
return _feedbackTextProperty;
}
set
{
_feedbackTextProperty = value;
RaisePropertyChanged(FeedbackTextPropertyName);
}
}
I am using a bindable application bar but when I click the button there is no value in the FeedbackText property. It looks as if "lostfocus" is not firing to update the property.
I am using MVVM Light. Have I missed something?
If you still had focus in the textbox when you clicked the app bar button the textbox won't fire the lost focus event and cause teh binding to update.
Yes, this can be frustrating. :(
There are various work arounds such as forcibly updating the binding in such a situation or the Binding Helper in the Coding4Fun Tools.
I hope that I am not too late. I had the same problem using Window Phone 8 saving the TextBox text when pressing an ApplicationBarIconButton. A way to fix this issue is to update the binding source property of the focused TextBox. You can do that with the following code:
var focusedObject = FocusManager.GetFocusedElement() as TextBox;
if (focusedObject != null)
{
var binding = focusedObject.GetBindingExpression(TextBox.TextProperty);
if (binding != null)
{
binding.UpdateSource();
}
}
Best!
In normal version of silverlight you can create an event handler by registering it by EventManager. Windows Phone 7 hasn't got that class.
My question is: How to create an event, which will be handled by the parent panels.
My scenario: I've created a custom class with some textbox in it. Foreach I've added my custom behavior, which raises when textblock is clicked. Behavior works like: "When this Textblock in custom control is clicked, please raise a custom event with my custom args (i want to pass them to the Custom Control itself (for example to specify to which VisualState change it)."
Can you help me how to handle my problem?
Could you provide sample code of what you are trying to do? it seems you want to create an event for when the TextBlock is clicked.
Add an event handler to the textblock:
public Event EventHandler<RoutedEventsArgs> TextClicked;
// Fire the event
private void OnTextClicked(object sender, RoutedEventArgs e)
{
if (TextClicked != null)
{
TextClicked(sender, e);
}
}
TextBlock.Click =+ OnTextBlockClicked;
private void OnTextBlockClicked(object sender, RoutedEventArgs e)
{
// Raise event
OnTextClicked(sender, e);
}
Something along those lines I think.