Situation :
i am creating a reminder app , i have two pages page1 (for displaying reminders) and page2(for accepting reminder data from user)
my page1.xaml.cs is :
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
IEnumerable<ScheduledNotification> notifications;
private void ResetItemsList()
{
notifications = ScheduledActionService.GetActions<ScheduledNotification>();
NotificationListBox.ItemsSource = notifications;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
//Reset the ReminderListBox items when the page is navigated to.
ResetItemsList();
}
}
}
and this is my logic for creating present in page2.xaml.cs
if ((bool)reminderRadioButton.IsChecked)
{
Reminder reminder = new Reminder(name);
reminder.Title = titleTextBox.Text;
reminder.Content = contentTextBox.Text;
reminder.BeginTime = beginTime;
reminder.ExpirationTime = expirationTime;
reminder.RecurrenceType = recurrence;
// Register the reminder with the system.
ScheduledActionService.Add(reminder);
}
PROBLEM :
In page1.xaml i have binded value of BeginTime property to text property of a textblock . now whenever i run my app i get date as well as time in textblock control , i want only time to be displayed in the textblock what should i do ?
You can format your string using StringFormat. An example would be
<TextBlock Text="{Binding BeginTime, StringFormat='t'}"
The output format would be 2:18 PM. You can modify the StringFormat string to what you like depending on how you want the output to appear. If one of the standard StringFormat specifier from the link above doesn't have the output you need, you can create a custom one.
Related
I am using Xamarin Forms to make Cross-platform application and I need to create simple view that user can choose date and time, similar to this:
View that I want to create that i found here: Picker in Xamarin iOS. Solution for Android is ready, but I need to create solution for iOS in the same application
I can not use standard date picker and another standard time picker separately from Xamarin Forms. I need to create custom solution (one view - simple choose both date and time).
I have tried to create view in Xamarin Forms that consist of 2 lists in horizontal orientation (one for date, one for time) but when I select one position, the list is not scrolling to the middle of view and also there is not auto-selecting middle position element when I scroll the list up or down. I want to create something that works like Xamarin-iOS solution: "Date and time picker" but in Xamarin Forms.
I have tried also to create in Xamarin-iOS part of project "date and time picker". I have main.storyboard and view controller but I dont know how to display view from Xamarin-iOS inside Xamarin Forms and pass selected date and time.
Can you help me, please?
If you want to implement date-time picker on Xamarin.Forms in iOS platform.You can use CustomRenderer.
in Forms
create a subclass of Picker
public class MyPicker:Picker
{
public MyPicker()
{
}
}
And add it in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<!-- Place new controls here -->
<local:MyPicker WidthRequest="150" BackgroundColor="AliceBlue"/>
</StackLayout>
in iOS
create the renderer of Picker .And you can set the format of picker as you want.
using System;
using Foundation;
using UIKit;
using ObjCRuntime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using xxx;
using xxx.iOS;
[assembly:ExportRenderer(typeof(MyPicker),typeof(MyPickerRenderer))]
namespace xxx.iOS
{
public class MyPickerRenderer:PickerRenderer
{
string SelectedValue;
public MyPickerRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
SetTimePicker();
}
}
void SetTimePicker()
{
UIDatePicker picker = new UIDatePicker
{
Mode = UIDatePickerMode.DateAndTime
};
picker.SetDate(NSDate.Now,true);
picker.AddTarget(this,new Selector("DateChange:"),UIControlEvent.ValueChanged);
Control.InputView = picker;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem done = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
picker.RemoveFromSuperview();
Control.ResignFirstResponder();
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { empty, done };
}
[Export("DateChange:")]
void DateChange(UIDatePicker picker)
{
NSDateFormatter formatter = new NSDateFormatter();
formatter.DateFormat = "MM-dd HH:mm aa"; //you can set the format as you want
Control.Text = formatter.ToString(picker.Date);
SelectedValue= formatter.ToString(picker.Date);
MessagingCenter.Send<Object, string>(this,"pickerSelected",SelectedValue);
}
}
}
And use MessagingCenter to pass the selected date and time.
public MainPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Object, string>(this, "pickerSelected", (sender, arg) => {
Console.WriteLine(arg);
//arg is the selected date and time
});
}
I have uploaded the demo on github .You can download it for test.
The effect
If you want to use native views in Xamarin.Forms it is possible, read it here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/native-views/
I am trying to make it so users can click a certain substring in a label and it would run a method, for example clicking #hashtag would run OpenHashtag(string hashtagand clicking a #taggedUser would run ViewProfile(taggedUser)
I found this tutorial, except I don't want phone numbers or URLs to be clickable, only hashtags and tagged users.
These are the renders its using
Android
[assembly: ExportRenderer(typeof(BodyLabel), typeof(BodyLabelAndroid))]
namespace SocialNetwork.Droid.Renderers
{
public class BodyLabelAndroid : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (BodyLabel)Element;
if (view == null) return;
TextView textView = new TextView(Forms.Context);
textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
textView.SetTextColor(view.TextColor.ToAndroid());
// Setting the auto link mask to capture all types of link-able data
textView.AutoLinkMask = MatchOptions.All;
// Make sure to set text after setting the mask
textView.Text = view.Text;
textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
// overriding Xamarin Forms Label and replace with our native control
SetNativeControl(textView);
}
}
}
IOS
[assembly: ExportRenderer(typeof(BodyLabel), typeof(BodyLabeliOS))]
namespace SocialNetwork.iOS.Renderers
{
public class BodyLabeliOS : ViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
var view = (AwesomeHyperLinkLabel)Element;
if (view == null) return;
UITextView uilabelleftside = new UITextView(new CGRect(0, 0, view.Width, view.Height));
uilabelleftside.Text = view.Text;
uilabelleftside.Font = UIFont.SystemFontOfSize((float)view.FontSize);
uilabelleftside.Editable = false;
uilabelleftside.DataDetectorTypes = UIDataDetectorType.All;
uilabelleftside.BackgroundColor = UIColor.Clear;
SetNativeControl(uilabelleftside);
}
}
}
Android:
Instead of using textView.AutoLinkMask = MatchOptions.All
you can use
Linkify.AddLinks method. Define your regular expression (for example, any word which starts with # or #) and it will work.
But on iOS, it is more complicated I think.
There I see two options:
Use WebView. Parse your string and add "<a href" where needed.
Break your text to pieces and add separate labels for each clickable part. If you want to click only hashtags and tagged users you can add the appropriate labels just below the text. Afterwards you can add tap gesture recognizers to handle the clicks.
Background: I have a button click event that triggers the parsing of text, selected at random [200], to be displayed to the user as a label.
Objective: Every day, display a new label text, selected at random, for the duration of that day until each text string has been used. Then repeat.
Example:
Today the user would see: COW
Tomorrow the user would see: PIG
Next Day the user would see: BEAR
Etc.
200-days later: Repeat
I am still quite new to all of this and would greatly appreciate any expertise. I’ve included my code below for reference. Thank you in advance for your time.
public partial class MainPage : ContentPage
{
string[] DailyAnimal_array;
Random r;
private long TimeSpan;
public MainPage()
{
InitializeComponent();
InitializeArrays();
}
private void InitializeArrays()
{
DailyAnimal_array = new string[200]
{
"COW! \r\n …”,
"SHEEP! \r\n ... ",
"PIG! \r\n … ",
"DOG! \r\n …”,
"CAT! \r\n ... ",
"BEAR! \r\n … ",
//etc. 200 total//
};
}
private async void AllDailyAnimals_Clicked(object sender, EventArgs args)
{
string DailyAnimal_Text = DailyAnimal_array[r.Next(200)];
this.DailyAnimal_Text.Text = DailyAnimal_Text;
AllDailyAnimals.IsEnabled = false;
Intro_Image.IsVisible = false;
AllDailyAnimals.IsVisible = false;
//ADD CODE HERE FOR 1 RANDOM NEW ANIMAL (PER DAY DURATION) UNTIL EACH ANIMAL USED-THEN REPEAT CODE//
}
XAML:
<Label x:Name="DailyAnimal_Text" Margin="10,10" TextColor="Purple"
FontAttributes="Bold" FontSize="Small" BackgroundColor="AliceBlue"
HorizontalTextAlignment="Center"/>
<Button x:Name="AllDailyAnimals" Text="CLICK"
Clicked="AllDailyAnimals_Clicked" Image="Animal_Button.png"
HorizontalOptions="Center" VerticalOptions="EndAndExpand"
WidthRequest="170" HeightRequest="170" TranslationX="100"
TranslationY="100" IsVisible="False"/>
From your question it would seem what you are looking for is how to persist data in your app.
Since your list of words seem to be pretty hard coded, you could probably get away with just saving a DateTime value to determine when the last value was selected and an index to the specific item. This way if your app is put into the background or killed of by the user, you will still know when the value was set and determine based on that whether a day has passed since a value last was selected.
To save simple values on iOS you would use NSUserDefaults, on Android SharedPreferences etc. Luckily nice people have abstracted that away for you in various plugins. What you may be looking for is the Xam.Plugins.Settings NuGet which handles all that stuff for you.
So add that package to your PCL and platform specific projects, then you can add values with:
CrossSettings.Current.AddOrUpdateValue("lastTime", DateTime.UtcNow);
And retrieve the time with:
var lastTime = CrossSettings.Current.GetValueOrDefault("lastTime", DateTime.MinValue);
Then if lastTime is DateTime.MinValue, it is the first time a user launched the app.
CrossSettings.Current.AddOrUpdateValue("lastTime", DateTime.UtcNow);
var index = r.Next(200);
CrossSettings.Current.AddOrUpdateValue("index", index);
Then use that index to set the text.
Now with those two set, you just need to handle the case when you come back to the App. You can simply do that by overriding OnAppearing() on your page and grab the lastTime from the settings and check against DateTime.UtcNow:
var lastTime = CrossSettings.Current.GetValueOrDefault("lastTime", DateTime.MinValue);
if (lastTime + TimeSpan.FromDays(1) > DateTime.UtcNow)
{
// grab new value from the array
// save the time
// save the index
// display it
}
With the help of Cheesebaron and God's good grace I finally got it. First, if you are using a Windows computer with the Xamarin Live player on an iPhone you need to change your Debug to Release in order for the Xam.Plugins.Settings to function properly. I also needed to add a tickcount. I pasted the code below that worked for me.
public partial class MainPage : ContentPage
{
string[] DailyAnimal_array;
Random r;
private long TimeSpan;
public MainPage()
{
InitializeComponent();
InitializeArrays();
}
private void InitializeArrays()
{
DailyAnimal_array = new string[200]
{
"BEAR! \r\n … ",
//etc. 200 total//
};
r = new Random(Environment.TickCount);
r.Next(0);
private async void AllDailyAnimals_Clicked(object sender, EventArgs args)
{
var lastTime=CrossSettings.Current.GetValueOrDefault("lastTime", DateTime.MinValue);
var index = r.Next(0,200);
if ((lastTime + TimeSpan.FromDays(1)) >= DateTime.UtcNow)
{
string DailyAnimal_Text = DailyAnimal_array[index];
this.DailyAnimal_Text.Text = DailyAnimal_Text;
CrossSettings.Current.AddOrUpdateValue("lastTime", DateTime.UtcNow);
CrossSettings.Current.AddOrUpdateValue("index", index);
}
else
{
index = CrossSettings.Current.GetValueOrDefault("index", index);
this.DailyAnimal_Text.Text = DailyAnimal_array[0];
}
AllDailyAnimals.IsEnabled = false;
Intro_Image.IsVisible = false;
AllDailyAnimals.IsVisible = false;
}
I have a TextBox in a Windows Desktop WPF application bound to a property of a ViewModel. Now the user focuses the TextBox and starts entering a new value. During this time a background process gets a new Value for the same Property (e.g. because another user in a multi user environment enters a new value and an observer is detecting and propagating this change) and calls a PropertyChanged event for this Property. Now the value changes and the stuff the current user just entered is lost.
Is there a built in way to prevent the change while the TextBox is focused? Or do I have to build my own solution?
I think a custom control is needed to achieve the behavior you describe. By overriding a couple methods on the default WPF TextBox, we can keep the user input even if the View Model changes.
The OnTextChanged method will be called regardless of how our textbox is updated (both for keyboard events and View Model changes), but overriding the OnPreviewKeyDown method will separate out direct user-input. However, OnPreviewKeyDown does not provide easy access to the textbox value because it is also called for non-printable control characters (arrow keys, backspace, etc.)
Below, I made a WPF control that inherits from TextBox and overrides the OnPreviewKeyDown method to capture the exact time of the last user key-press. OnTextChanged checks the time and updates the text only if both events happen in quick succession.
If the last keyboard event was more than a few milliseconds ago, then the update probably did not happen from our user.
public class StickyTextBox : TextBox
{
private string _lastText = string.Empty;
private long _ticksAtLastKeyDown;
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
_ticksAtLastKeyDown = DateTime.Now.Ticks;
base.OnPreviewKeyDown(e);
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (!IsInitialized)
_lastText = Text;
if (IsFocused)
{
var elapsed = new TimeSpan(DateTime.Now.Ticks - _ticksAtLastKeyDown);
// If the time between the last keydown event and our text change is
// very short, we can be fairly certain than text change was caused
// by the user. We update the _lastText to store their new user input
if (elapsed.TotalMilliseconds <= 5) {
_lastText = Text;
}
else {
// if our last keydown event was more than a few seconds ago,
// it was probably an external change
Text = _lastText;
e.Handled = true;
}
}
base.OnTextChanged(e);
}
}
Here's a sample View Model which I used for testing. It updates its own property 5 times from a separate thread every 10 seconds to simulate a background update from another user.
class ViewModelMain : ViewModelBase, INotifyPropertyChanged
{
private delegate void UpdateText(ViewModelMain vm);
private string _textProperty;
public string TextProperty
{
get { return _textProperty; }
set
{
if (_textProperty != value)
{
_textProperty = value;
RaisePropertyChanged("TextProperty");
}
}
}
public ViewModelMain()
{
TextProperty = "Type here";
for (int i = 1; i <= 5; i++)
{
var sleep = 10000 * i;
var copy = i;
var updateTextDelegate = new UpdateText(vm =>
vm.TextProperty = string.Format("New Value #{0}", copy));
new System.Threading.Thread(() =>
{
System.Threading.Thread.Sleep(sleep);
updateTextDelegate.Invoke(this);
}).Start();
}
}
}
This XAML creates our custom StickyTextBox and a regular TextBox bound to the same property to demonstrate the difference in behavior:
<StackPanel>
<TextBox Text="{Binding TextProperty, UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
<TextBlock FontWeight="Bold" Margin="5" Text="The 'sticky' text box">
<local:StickyTextBox Text="{Binding TextProperty}" MinWidth="200" />
</TextBlock>
</StackPanel>
I am creating an app where we have a Data Template list of building names and when clicked on say "Thomas Gosnell Hall", it will go to a new page with the TextBlock changed to that of the selected building name "Thomas Gosnell Hall". I know data binding is used to do this, but how do I do it across two different pages?
MainPage.xaml
<TextBlock Tap="TextBlock_Tap" Text="{Binding LineOne}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
MainPage.xaml.cs (When a user taps on the building name, it will navigate to new page)
public void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var building = ((TextBlock)sender).Text; // gets information based on the tapped building
//perform action based on information about the tapped building
if(building == "Thomas Gosnell Hall")
{
//MessageBox.Show("08 - (GOS)");
NavigationService.Navigate(new Uri("/MapLocation.xaml?building=" + building, UriKind.Relative)); // pass the string value to destination page through Uri parameter
}
else if(building == "Lyndon Baines Johnson Hall")
{
MessageBox.Show("060 - (LBJ)");
}
}
MapLocation.xaml
<TextBlock x:Name="buildingName" Text="Building Name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
MapLocation.xaml.cs (The new page after the user selected the building name)
/**
* How to: Create the Binding (behind code)
* Source: http://msdn.microsoft.com/en-us/library/cc838207%28v=vs.95%29.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
*/
// Define source object
public class Building
{
public string BuildingName { get; set; }
}
public MapLocation()
{
InitializeComponent();
Loaded += MapLocation_Loaded;
/* // create an instance of the source object
Building bldg = new Building();
bldg.BuildingName = building; // value to change depending on user's click
// create a binding object
Binding MyBinding = new Binding();
// set the binding properties on the binding object
MyBinding.Path = new PropertyPath("BuildingName");
MyBinding.Mode = BindingMode.OneTime;
// set the source of the binding by setting the DataContext property
buildingName.DataContext = bldg;
// attach the binding to the property of the FrameworkElement
buildingName.SetBinding(TextBlock.TextProperty, MyBinding);*/
}
private void MapLocation_Loaded(object sender, RoutedEventArgs e)
{
//throw new NotImplementedException();
string building;
if(NavigationContext.QueryString.TryGetValue("building", out building))
{
//load information based on building parameter value
buildingName.Text = building;
}
}
/*public MapLocation_Loaded()
{
string building;
if(NavigationContext.QueryString.TryGetValue("building", out building))
{
//load information based on building parameter value
}
}*/
The problem lies within this line bldg.BuildingName = building; as it says The name building does not exist in the current context. It exists in the MainPage.xaml.cs, but not in MapLocation.xaml.cs. How do I data bind the building name depending on the user's tapped choice of building onto the next page?
I would suggest to pass the string value to destination page through Uri parameter :
public void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var building = ((TextBlock)sender).Text;
NavigationService.Navigate(new Uri("/MapLocation.xaml?building=" + building, UriKind.Relative));
}
Then handle loading correct informations in the destination page, for example in page Loaded event handler :
public MapLocation_Loaded()
{
string building;
if(NavigationContext.QueryString.TryGetValue("building", out building))
{
//load information based on building parameter value
}
}
One option is to expose the selected value on MainPage as a public property. Then other pages can just read whatever value was set.
The other option is to pass it as state in the navigate method:
NavigationService.Navigate(new Uri("/MapLocation.xaml", UriKind.Relative), building);
see here: http://msdn.microsoft.com/en-us/library/ms591042(v=vs.110).aspx