I have one chm for my application which i want to attach with my application that is when user press F1 attached help with the project opens up.
I do not know of any in built support in WPF to display CHM files. What I do is add an InputGesture to connect F1 keystroke to Application.Help command and in the Windows CommandBindings add a handler for Application.Help Command. Here is a sample code:
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<Window.InputBindings>
<KeyBinding Command="Help" Key="F1"/>
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Help" Executed="HelpExecuted" />
</Window.CommandBindings>
<Grid>
</Grid>
Here's the handler code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
System.Diagnostics.Process.Start(#"C:\MyProjectPath\HelpFile.chm");
}
}
Using F1 Help (CHM format) With WPF
Based on this approach, I did the following so I could take advantage of an OnlineHelpViewModel I had that was managing the help through a RelayCommand. When F1 is pressed, with this approach, the RelayCommand on the viewmodel is invoked just as if ia ? button had been pushed. In other words,we bind F1 to the RelayCommand.
This example uses GalaSoft MvvmLight.
DependencyProperty on the MainWindow
public static DependencyProperty HelpCommandProperty = DependencyProperty.Register("HelpCommand",
typeof(RelayCommand<string>), typeof(WindowExt),
new PropertyMetadata(null));
public RelayCommand<string> HelpCommand
{
get
{
return (RelayCommand<string>)GetValue(HelpCommandProperty);
}
set
{
SetValue(HelpCommandProperty, value);
}
}
OK that holds the command
Now in the window loaded event or somewhere you like:
...
Binding b2 = new Binding();
b2.Source = ViewModelLocator.OnlineHelpViewModelStatic;
b2.Path = new PropertyPath("ShowApplicationHelpCommand");
b2.Mode = BindingMode.OneWay;
this.SetBinding(HelpCommandProperty, b2);
var kb = new KeyBinding();
kb.Key = Key.F1;
kb.Command = HelpCommand;
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, HelpCommand_Executed));
OK that binds the command on the SOURCE viewmodel to this window.
Then a handler for the command on this window ( perhaps this can be inline somehow)
private void HelpCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
this.HelpCommand.Execute(HelpContextGuid);
}
and you now can call the single help command on OnlineHelpViewModel from anywhere, and it can be arbitrarily complicated too depending. Note that the DP HelpContextGuid is passed - it is up to the command to decide what to do with it but the RelayCommmand<string> wants an argument
The command itself looks like (on the SOURCE Viewmodel)
...
ShowApplicationHelpCommand = new RelayCommand<string>(
(h) => { ShowApplicationHelp(h); },
(h) => CanShowApplicationHelpCommand);
...
and method it invokes is whatever it takes to show the help,
In my case, I create a RadWindow and so on and populated it with XamlHelp using the BackSpin Software HelpLoader. The help file is generated from Word with Twister4Word.
All of this is particular to my application so you would probably do something else to make a help window. Here is the constructor:
public MigratorHelpWindow()
{
// create local resources for desingn mode, so Blend can see the viewmodels
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
App.CreateStaticResourcesForDesigner(this);
}
InitializeComponent();
if (Application.Current.MainWindow != null)
{
var thm = ThemeManager.FromName(Application.Current.FindResource("TelerikGlobalTheme").ToString() ?? "Office_Blue");
StyleManager.SetTheme(this, thm);
}
// window configuration
MaxHeight = SystemParameters.WorkArea.Height;
MaxWidth = SystemParameters.WorkArea.Width;
Binding b = new Binding();
b.Source = ViewModelLocator.OnlineHelpViewModelStatic;
b.Path = new PropertyPath("ApplicationHelpFileName");
b.Mode = BindingMode.OneWay;
this.SetBinding(ApplicationHelpFileNameProperty, b);
if (String.IsNullOrEmpty(ApplicationHelpFileName))
{
UiHelpers.ShowError("No help file is available", true);
return;
}
// LOAD YOUR HELP HERE OR WHATEVER
// LOAD YOUR HELP HERE OR WHATEVER
// LOAD YOUR HELP HERE OR WHATEVER
HelpLoader.Load(ApplicationHelpFileName);
HelpLoader.Default.Owner = this;
HelpLoader.Default.HelpLayout = HelpLayout.Standard;
HelpLoader.Default.TocContainer = _mTOC;
HelpLoader.Default.IndexContainer = _mIndex;
HelpLoader.Default.TopicContainer = _mTopic;
HelpLoader.Default.SearchContainer = _mSearch;
HelpLoader.Default.FavoritesContainer = _mFavorites;
}
You can find the BackSpin Help Authoring tool here
http://www.backspinsoftware.com/site/Default.aspx
It generates compiled help from Word documents.
Related
I am simply trying to move to a new line when Return + Shift are pressed.
I got this much from a previous post on here:
<TextBox.InputBindings>
<KeyBinding Key="Return" Modifiers="Shift" Command=" " />
</TextBox.InputBindings>
But I cannot find anywhere that explains how to accomplish the move to a new line within the textbox.
I cannot use the: AcceptsReturn="True" as I want return to trigger a button.
If you don't have an ICommand defined, you can attach a handler for the UIElement.PreviewKeyUp event to the TextBox. Otherwise you will have to define an ICommand implementation and assign it to the KeyBinding.Command so that the KeyBinding can actually execute. Either solution finally executes the same logic to add the linebreak.
You then use the TextBox.AppendText method to append a linebreak and the TextBox.CaretIndex property to move the caret to the end of the new line.
MainWindow.xaml
<Window>
<TextBox PreviewKeyUp="TextBox_PreviewKeyUp" />
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
private void TextBox_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (!e.Key.Equals(Key.Enter)
|| !e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Shift))
{
return;
}
var textBox = sender as TextBox;
textBox.AppendText(Environment.NewLine);
textBox.CaretIndex = textBox.Text.Length;
}
}
I found a nice way to do it without using an ICommand.
Simply added this PreviewKeyDown event onto the control in xaml:
PreviewKeyDown="MessageText_PreviewKeyDown"
And this is the C# behind:
private void MessageText_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
// Get the textbox
var textbox = sender as TextBox;
// Check if we have pressed enter
if (e.Key == Key.Enter && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
{
// Add a new line where cursor is
var index = textbox.CaretIndex;
// Insert a new line
textbox.Text = textbox.Text.Insert(index, Environment.NewLine);
// Shift the caret forward to the newline
textbox.CaretIndex = index + Environment.NewLine.Length;
// Mark this key as handled by us
e.Handled = true;
}
}
I have a parameterised constructor in My Application. I want to add controls dynamically to my silverlight Child Control Page. But it gives NullReferenceException.
I can't find out why it returns null.Can any help me with this situation?
public PDFExport(FrameworkElement graphTile1, FrameworkElement graphTile2,FrameworkElement graphTile3)
{
Button btnGraph1 = new Button();
string Name = graphTile1.Name;
btnGraph1.Content = Name;
btnGraph1.Width = Name.Length;
btnGraph1.Height = 25;
btnGraph1.Click += new RoutedEventHandler(btnGraph1_Click);
objStack.Children.Add(btnGraph1);
LayoutRoot.Children.Add(objStack); // Here am getting null Reference Exception
_graphTile1 = graphTile1;
_graphTile2 = graphTile2;
_graphTile3 = graphTile3;
}
Thanks.
I guess objStack is a stackpanel declared in your XAML?
Be aware that the UI component of your xaml are build by the call to InitializeComponent.
Thus objStack will not exist until you call InitializeCOmponent() in your constructor.
Also, you should know that the call to InitializeComponent is asynchronous, so you code should look like something like that:
private readonly FrameworkElement _graphTile1;
private readonly FrameworkElement _graphTile2;
private readonly FrameworkElement _graphTile3;
public PDFExport(FrameworkElement graphTile1, FrameworkElement graphTile2, FrameworkElement graphTile3)
{
_graphTile1 = graphTile1;
_graphTile2 = graphTile2;
_graphTile3 = graphTile3;
}
private void PDFExport_OnLoaded(object sender, RoutedEventArgs e)
{
Button btnGraph1 = new Button();
string Name = _graphTile1.Name;
btnGraph1.Content = Name;
btnGraph1.Width = Name.Length;
btnGraph1.Height = 25;
btnGraph1.Click += new RoutedEventHandler(btnGraph1_Click);
objStack.Children.Add(btnGraph1);
LayoutRoot.Children.Add(objStack);
}
Hope it helps.
As per my research i got that, why it raises an exception: Because there is no
InitializeComponent() in My Constructor and am not calling parent constructor.
That is the reason it raises Exception.
Just Add InitializeComponent() to the code, simple
This has me stumped - hopefully someone can point out an obvious error. I have a user control that I am adding to a grid in the MainView of my program. Main view is bound to MainViewModel and the usercontrol is bound to CardioVM.
I have used a test label to check that the routing of the user control is correct and all work ok. I have a class named Cardio which has a property of
List<string> exercises
I am trying to pass the strings in
Cardio.List<string> exercises
to a
List<string> CardioList
in my CardioVM. When debugging
List<string> CardioList
is getting populated with items from
Cardio.List<string> exercises
but my ComboBox is not displaying the items on screen. Here is xaml for my UserControl and :
<UserControl x:Class="CalendarTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding CardioVM, Source={StaticResource Locator}}">
<Grid>
<ComboBox ItemsSource="{Binding CardioList, Mode=OneWay}" SelectedItem="{Binding SelectedCardio, Mode=TwoWay}" Height="50"></ComboBox>
</Grid>
and here is the code for my CardioVM:
public class CardioVM : ViewModelBase
{
public Cardio cardioItem { get; set; }
public CardioVM()
{
TestLabel = "Tester";
}
//Test Label for binding testing
private string testLabel;
public string TestLabel
{
get { return testLabel; }
set
{
testLabel = value;
RaisePropertyChanged("TestLabel");
}
}
public CardioVM(string Date, string File)
{
cardioItem = new Cardio(File, Date);
CardioList = new List<string>(cardioItem.exercises);
}
private List<string> cardioList;
public List<string> CardioList
{
get { return cardioList; }
set
{
cardioList = value;
RaisePropertyChanged("CardioList");
}
}
private string _selectedCardio;
public string SelectedCardio
{
get { return _selectedCardio; }
set
{
_selectedCardio = value;
RaisePropertyChanged("SelectedCardio");
}
}
}
}
Not sure where I am going wrong here but any pointers would be much appreciated.
Here is where I thought I was adding in the userControl to a content control bound proprty in my Main view model:
public void NewTemplateExecute()
{
TextHideTab = "Close";
NewTemplateType = ("New " + SelectedExercise + " Exercise Template");
//Set the message and lists based on the exercise selected plus adds the drop down control
switch (SelectedExercise)
{
case "Cardio":
///
//This is where I thought CardioVM was being added
///
NewTemplateText = "Please choose a cardio exercise from the drop down list to the left. You can then select the duration of the exercise and the intensity. To add another exercise please press the plus button in the right hand corner";
ExerciseDropDowns = new CardioVM(selectedDateLabel, #"Model\Repository\Local Data\CardioList.txt");
break;
case "Weights":
NewTemplateText = "Please select a exercise type. you can refine your exercises by body area. Then add the number of sets and the reps per set. Add as many exercises as you like - dont forget to set to total duration";
break;
case "HIIT":
NewTemplateText = "HIIT to add";
break;
}
Messenger.Default.Send("NewTemplate");
}
I had set the datacontext for CardioVM in my Mainwindow xaml as:
<DataTemplate DataType="{x:Type local:CardioVM}">
<view:UserControl1/>
</DataTemplate>
I presume I have made a mistake in the way that I have hooked up CaridoVM but couldn't seem to get it to databind unless I sent it through the VM locator
Thanks to nemesv - you were of course spot on. Removed the DataContext from my CardioVM and now just using DataTemplate set in Main view to bind the Cardio view to the ViewModel. I can now call a cardioVM with parameters from the Mainview and it populates my combobox as expected. Seems I nbeeded to touch up on some of the basics of MVVM
I have 2 windows w1 and w2 with one textbox each and data in txtbox1 in w1 needs to be appearing in w2 txtbox .How to achieve this using dependency properties
I suggest you rather use a common DataContext for your two windows than binding them to each other. As an example, let's assume you have this class as DataModel:
public class MyDataModel : INotifyPropertyChanged
{
private string text;
public string Text {
get { return text; }
set {
text = value;
RaisePropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can set it as DataContext on your windows:
MyDataModel model = new MyDataModel();
model.Text = "Hello World";
MyWindow a = new MyWindow();
MyOtherWindow b = new MyOtherWindow();
a.DataContext = model;
b.DataContext = model;
If you have done this, you can set the Text Property of the TextBoxes in every window to a Binding like this:
<TextBox Text="{Binding Text}"/>
Both Textboxes will now automatically update if you set model.Text to another value.
Is there a mother of the 2 forms? a form that creates both of them?
In this case what I normally do, since I'm implementing INotifyOnPropertyChanged anyway, is in the mother I subscribe to the w1's PropertyChanged event, get the value, and then place it in the property of w2.
I wouldnt really use Dependency Properties
I know its not pretty but I generally prefer it because I like to keep the 1 viewmodel to 1 view relationship. My boss thinks its easier to read and understand this way.
I am doing something wrong .. you know how it is.
I have tried playing around with ItemsSource , DataContext , DisplayMemberPath and SelectedValuePath and I either get a blank list of a list of the ToString method being called in the Person object;
WHAT WOULD REALLY HELP is for someone to publish an answer that works for this example.
I have simplified the problem as I am having difficulty in general with databinding generics.
I have created a simple Generic List of Person and want to bind it to a combo. (also want to try use a ListView too).
I either get a list of blanks or a list of 'xxxx.Person' where xxxx = namespace
<Window x:Class="BindingGenerics.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<ComboBox Name="ComboBox1"
ItemsSource="{Binding}"
Height="50"
DisplayMemberPath="Name"
SelectedValuePath="ID"
FontSize="14"
VerticalAlignment="Top">
</ComboBox>
</Grid>
</Window>
using System.Windows;
using System.ComponentModel;
namespace BindingGenerics
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Person p = new Person();
// I have tried List and BindingList
//List<Person> list = new List<Person>();
BindingList<Person> list = new BindingList<Person>();
p.Name = "aaaa";
p.ID = "1111";
list.Add(p);
p = new Person();
p.Name = "bbbb";
p.ID = "2222";
list.Add(p);
p = new Person();
p.Name = "cccc";
p.ID = "3333";
list.Add(p);
p = new Person();
p.Name = "dddd";
p.ID = "4444";
list.Add(p);
ComboBox1.DataContext = list;
}
}
public struct Person
{
public string Name;
public string ID;
}
}
In your code sample, Person.Name is a field rather than a property. WPF data binding considers only properties, not fields, so you need to change Person.Name to be a property.
Change your Person declaration to:
public class Person
{
public string Name { get; set; }
public string ID { get; set; }
}
(For production code, you'll probably want to use an ObservableCollection<Person> rather than a List<Person> and either make Person immutable or make it implement INotifyPropertyChanged -- but those aren't the sources of your immediate problem.)
In the code shown you're setting ItemsSource twice, the first time in XAML (called by InitializeComponent) to the DataContext of ComboBox1, which can't be determined from what you've posted but it's probably not what you want. After that you're resetting it from code to your list object (here with typos). In this code you're also adding the same instance of Person 4 times and just changing its Name and ID over and over. I suspect a combination of these issues and the fact that you're using a List instead of ObservableCollection are causing the issues in your application.
It would help narrow it down if you could post some actual code you're seeing problems with as what you've put here isn't even compilable.
Well... I'm assuming your actual code has corrected syntax, as the code you pasted in won't compile.
I put this code into a new WPF app and, after new-ing each Person object, my combobox populated fine. You might want to move your population code into a Loaded event, which will ensure the form is properly constructed. Here's the corrected xaml and codebehind (with a few syntax shortcuts):
xaml:
<Grid>
<ComboBox Name="ComboBox1" Height="70"
DisplayMemberPath="Name"
SelectedValuePath="ID" />
</Grid>
codebehind:
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
var list = new List<Person>();
Person p = new Person(){Name = "aaaa",ID = "1111"};
list.Add(p);
p = new Person(){Name = "bbbb", ID="2222"};
list.Add(p);
p = new Person(){Name = "cccc", ID="3333"};
list.Add(p);
p = new Person(){Name = "dddd", ID="4444"};
list.Add(p);
ComboBox1.ItemsSource = list;
}