How to show a Content Dialog with multiple input fields from viewmodel - c#

I am building an C# UWP application where onclick of a button in the view a command (using System.Windows.Input.IComand) raises in viewmodel(not code behind) which builds a form of input fields like Name:____ phone:____ etc and shows it on the UI/view.
I dont want to keep the <ContentDialog></ContentDialog> in the
view.
What have I done so far is
Button in my view:
<CommandBar>
<AppBarButton x:Name="buttonNew" Command="{Binding AddClick}" />
</CommandBar>
ViewModel object in the code behind is set as the DataContext of the
View
In the view model:
public ICommand AddClick=> new RelayCommand(Add);
private async void Add()
{
TextBox input = new TextBox()
{
PlaceholderText = "Name",
};
var contentDialog = new ContentDialog
{
Title = "Add a Person",
Content = input,
FullSizeDesired = true,
PrimaryButtonText = "Add",
CloseButtonText = "Cancel"
};
await contentDialog.ShowAsync();
}
As you can see I am able to show/trigger a Content Dialog box with one input field and 2 buttons.
My problem is:
How to add more input fields like we add in a
<StackPanel></StackPanel> in the code of view model and assign it
to the content of the Content Dialog?
How to size it accordingly that all the fields show up properly and does not show up haphazard since am not coding this in the Xaml?

From the general application construction practice, it is recommended to create a UI using XAML to create a custom ContentDialog.
You don’t have to write the XAML code of ContentDialog in the View, you can add new item in Visual Studio, select the Content Dialog template, and create a custom dialog derived from ContentDialog.
Then use code similar to the following:
MyCustomDialog.xaml
<ContentDialog
...
Title="Add a Person"
FullSizeDesired="True"
PrimaryButtonText="Add"
CloseButtonText="Cancel"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<StackPanel>
<TextBox Header="Name" x:Name="NameBox" PlaceholderText="Name"
HorizontalAlignment="Stretch"/>
<TextBox Header="Phone" x:Name="PhoneBox" PlaceholderText="Phone"
Margin="0,15,0,0" HorizontalAlignment="Stretch"
InputScope="Number"/>
</StackPanel>
</ContentDialog>
ViewModel.cs
private async void Add()
{
var dialog = new MyCustomDialog();
await dialog.ShowAsync();
}
If you insist on using C# code to create ContentDialog, you need to convert the tags in XAML into corresponding classes, but this method is not easy to debug.
private async void Add()
{
var container = new StackPanel();
TextBox nameBox = new TextBox()
{
PlaceholderText = "Name",
Header = "Name",
HorizontalAlignment = HorizontalAlignment.Stretch
};
TextBox phoneBox = new TextBox()
{
PlaceholderText = "Phone",
Header = "Phone",
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 15, 0, 0)
};
container.Children.Add(nameBox);
container.Children.Add(phoneBox);
var contentDialog = new ContentDialog
{
Title = "Add a Person",
Content = container,
FullSizeDesired = true,
PrimaryButtonText = "Add",
CloseButtonText = "Cancel"
};
await contentDialog.ShowAsync();
}
The tags in XAML are actually the corresponding classes in C#, from the code you provided, the ContentDialog is displayed in full screen. You mentioned that you want the fields to be displayed correctly, if you mean that the string entered in the TextBox is too long and the text is not displayed completely, you can set the TextBox.TextWrapping property to True.

Related

How to update content of dialoghost in wpf material design?

This error happen when I open new usercontrol into dialoghost during opening dialoghost :
when I press submit button
this is my code xaml:
MainWindow:
<Grid>
<Button Content="Show"
Command="{Binding OpenRegisCommand}"
VerticalAlignment="Bottom"
Margin="0 0 0 40"
Foreground="White"
Width="100"
Background="#23A9F2">
</Button>
<md:DialogHost Identifier="RootDialogHostId">
</md:DialogHost>
</Grid>
my usercontrol and my mainwindow using 1 viewmodel:
public MainViewModel()
{
OpenRegisCommand = new DelegateCommand(OpenFormRegis);
Submit = new DelegateCommand(Check);
}
private async void Check()
{
if(Name.Equals("admin")&&Pass.Equals("123456"))
{
var view = new DialogOk();
await DialogHost.Show(view, DialogHostId);
}
else
{
var view = new DialogNo();
await DialogHost.Show(view, DialogHostId);
}
}
private async void OpenFormRegis()
{
var view = new FormRegis();
await DialogHost.Show(view, DialogHostId);
}
button submit in my usercontrol binding to DelegateCommand Submit in my viewmodel
Each dialog hosts can be used to show one view at a time.
If you have two views that you wanted to shot at the same time (less likely), you will need two dialog hosts.
In your case, if you're trying to open the "OpenFormRegis" window in your dialog host, I would suggest you to use Windows instead.
In my case, I did the following:
1- Get the dialogs that are active, using DialogHost.GetDialogSession("RootDialog").
2- If it is different from null, set the content with the UpdateContent(alertBox) method which updates the content of the dialog.
3- If it is null, establish the usual flow to show the content of the dialog.
AlertBoxView alertBox = new AlertBoxView();
alertBox.DataContext = this;
var dialog = DialogHost.GetDialogSession(IdentifierDialog);
if (dialog != null)
{
dialog.UpdateContent(alertBox);
}
else
{
await DialogHost.Show(alertBox, IdentifierDialog);
}
*AlertBoxView: is my custom view of DialogHost
*IdentifierDialog: is the variable that takes the Identifier of the dialog

Instantiating a new UserControl programmatically while setting a DataContext to facilitate binding

Starting with the MahApps-Material mashup demo, I'm trying to use a button click event to create a new TabItem from my Views. For now, the CustomTabItem will show text bound to some property from a FancyObject (being served to the View from my FancyTabViewModel). But I've got the DataContext, dependency property or the Binding done wrong.
public void NewTabOnClick(object sender, RoutedEventArgs e)
{
// create my new object from Models
FancyObject fo = new FancyObject();
// create the INofify VM and pass it my object
// the VM has a public VMFancyObject property to serve the fo
FancyTabViewModel fvm = new FancyTabViewModel(fo);
// create the new UserControl and set its context to the VM
CustomTabItem newTab = new CustomTabItem() {
Header = "New tab"
};
newTab.DataContext = fvm;
MainWindowTabs.Items.Add(newTab);
}
And in my <TabItem x:Class="MyProject.Views.CustomTabItem" there is a label so bound: <Label Content="{Binding VMFancyObject.SomeList.Count}"/>
I expect to see the default count of the List created in the FancyObject's constructor. However, after the new tab is created and added to the dragablz:TabablzControl, I just see a blank label.
I also tried <Label DataContext="{Binding Path=DataContext.FancyTabViewModel,RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" Content="{Binding VMFancyObject.SomeList.Count}" HorizontalAlignment="Left" Margin="341,196,0,0" Foreground="Black" Background="#FF97FF02"/>

Xamarin.Forms set focus from mvvm ViewModel

I'm working on a chat application using Xamarin.Forms.
And I want to avoid to hide the keyboard when the Entry loses focus and button Send is clicked.
How can I do it on Android and iOS?
I use XF, full Mvvm without XAML(only C#)
Updated:
In page class:
private EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
In model class:
var entry = CurrentPage.FindByName<EntrySetBorder>("_newMessageEntry");
entry.Focus();
}
This can be achieved easily by using the FindByName<>() function inside the PCL.
This is one way of doing that:
Entry myEntry = CurrentPage.FindByName<Entry>("YourEntryName");
myEntry.Focus();
You can add that at the end of the click handler of your send button.
Edit:
In your case I think your problem is that your entry is set to private, so I would suggest either expose it as public or expose it using another public property. Two solutions that might work:
public EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
And:
EntrySetBorder entry = CurrentPage.FindByName<EntrySetBorder>("_newMessageEntry");
entry.Focus();
Or you go with this:
private EntrySetBorder _newMessageEntry;
...
_newMessageEntry = new EntrySetBorder
{
TextColor = Color.Black,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.End,
Margin = new Thickness(0, 0, 5, 0)
};
public EntrySetBorder NewMessageEntry => _newMessageEntry;
and :
EntrySetBorder entry = CurrentPage.FindByName<EntrySetBorder>("NewMessageEntry");
entry.Focus();
Please try that :)
Edit 2:
After reviewing your code, and testing it, the final way to fix it was by sending the Entry as a parameter in the command you're using, example:
Inside the page you're creating:
sendButton.CommandParameter = NewMessageEntry; // We're adding the Entry we want to focus as a command parameter.
And inside your PageModel and the command we want to use:
public Command SendCommand
{
get
{
return new Command<Entry>((obj) => //obj here means the parameters we're sending I.E: the entry we set it in the page.
{
//The code you want to execute
Entry entry = obj;
entry.Focus();
});
}
}
Note that I used Entry because I didn't have all the implementation of your custom entry.
This is an example of how I do, before this I used to do using MessagingCenter
in xaml , you need to give an x:Name to the obj you want to make focus.
<!-- PICKER's DEFINITION -->
<DatePicker
x:Name="Datepicker"
Date="{Binding SelectedDate, Mode=TwoWay}"
IsEnabled="true"
IsVisible="false">
</DatePicker>
then you have to make reference to that control in your command parameter on a button or for example in this case I use a toolbar item.
<!-- MENU TOOLBAR -->
<ContentPage.ToolbarItems>
<ToolbarItem
Command="{Binding ShowCalendarCommand}"
Icon="Calendar"
CommandParameter="{x:Reference Datepicker}" />
</ContentPage.ToolbarItems>
then in your vm command :
#region toolbar commands
public ICommand ShowCalendarCommand => new RelayCommand<Object>(ShowCalendar);
#endregion
private void ShowCalendar(Object obj)
{
var calendar = (DatePicker)obj;
calendar.Focus();
// MessagingCenter.Send(this, "Calendar");
}

How to create multiple buttons from existing strings in .txt file

I wonder how I can create buttons in my Toolbar by reading lines from a .txt file.
For example:
//bookmarks.txt
http://example.com
http://example2.com
http://example3.com
...
What I want is that my program on start should create a button for each line in my .txt with this event:
public void Button_Click(object sender, RoutedEventArgs e) //fire bookmark event
{
string text = e.Source.ToString().Replace("System.Windows.Controls.Button: ", "");
WebBrowser1.Navigate(text);
}
UPDATE
This is how I read the .txt:
for (int i = 0; i < File.ReadLines(#"bookmarks.txt").Count(); i++)
{
//Add button right here
}
You're trying to use WPF as if it were WinForms. This is how you would fulfil your requirements in WPF... first create a DependencyProperty collection in your Window code behind and populate it with your text entries:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(YourWindowOrUserControl));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
...
Items = new ObservableCollection<string>(File.ReadLines(#"bookmarks.txt"));
Then you simply data bind the collection to the ToolBar.ItemsSource property and declare a DataTemplate to define what each string should look like... in your case, we'll set it as the text in a Button:
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Margin="1,0,0,0" />
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
Of course, you'll need to set the Window.DataContext to the class with your properties... the simplest way is to set it in the code behind constructor like this:
public YourWindowOrUserControl
{
InitializeComponent();
DataContext = this;
}
You must read up about how to set the DataContext properly though, as setting it this way is easy, but not necessarily correct.
Finally, you could create a class with all the necessary properties for the Button... for example, you could add a property named Text and another called Command and then make your Items property a collection of those. Then you could data bind to it like this:
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" Command="{Binding Command}" Margin="1,0,0,0" />
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
You can create buttons dynamic and add click event on fly:
Button btn = new Button();
btn.Location = new Point(yourX, yourY);
btn.Font = new Font(btn.Font.Name, 10);
btn.Text = "Text from your txt file here";
btn.ForeColor = Color.SeaShell; // choose color
btn.AutoSize = true;
btn.Click += (sender, eventArgs) =>
{
string text = btn.Text.Replace("System.Windows.Controls.Button: ", "");
WebBrowser1.Navigate(text);
};
(Insert this code in your For. Btw, you can replace the for with while. see this link)

Add StackPanel and more than one TextBlock inside Button

I want to add a number of TextBlocks inside a Button. How can I add a number of them, along with StackPanels or Canvases, in C#, as shown below in XAMAL
<Button>
<StackPanel>
<TextBlock Text="ABC"/>
<TextBlock Text="DEF"/>
</StackPanel>
</Button>
It's easy:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var tb1 = new TextBlock() { Text = "TextBlock 1" };
var tb2 = new TextBlock() { Text = "TextBlock 2" };
var stackPanel = new StackPanel();
stackPanel.Children.Add(tb1);
stackPanel.Children.Add(tb2);
var button = new Button() { Content = stackPanel };
this.Content = button;
}
}
Maybe you should think about an enclosing control of csharpfolk's answer. This would help to get a reuseable control.
The text strings are good to use as a dependency property. :)
Regards,
- Tobbo

Categories