Alternative to hyperlink to navigate? - c#

I'm using WPF (.net 4.5 target) and while i'm quite used to WPF this is the first time i'm working with the page / navigation model.
I see that to navigate to a page i can do something very simple (use an hyperlink) however this is very far from what i want in terms of style (i'd rather start from a button). Is there an alternative to hyperlinks or is my best shot to go with a button + code behind to navigate?
My goal is to provide styled buttons with icons as hyperlinks within a uniformgrid that takes the whole page for the home page.

In Silverlight/WP, there is HyperlinkButton, which is exactly what you're asking for, but apparently that does not exist in WPF. So, it seems that your best bet would be to use the regular Button, along with NavigationService.Navigate in its Command. So, something like this:
<Button Content="Navigate" Command="{Binding NavigateCommand}"
CommandParameter="/Views/SomePage.xaml" />
Then NavigateCommand would be a standard DelegateCommand, using the parameter as the URI:
public class MyViewModel : ViewModelBase
{
public ICommand NavigateCommand { get; private set; }
public MyViewModel()
{
NavigateCommand = new DelegateCommand<string>(url => {
var uri = new Uri(url, UriKind.Relative);
NavigationService.Navigate(uri);
});
}
}
You may instead want to write your own version of HyperlinkButton, which would go something like this:
public class HyperlinkButton : Button
{
public string NavigateUri { get; set; }
protected override void OnClick(EventArgs e)
{
if (NavigateUri != null)
{
var uri = new Uri(NavigateUri, UriKind.Relative);
NavigationService.Navigate(uri);
}
}
}

Related

Detecting tab changed event

I am working on xamarin.forms. I need to detect the event of tab being changed either by swapping left or right or by clicking without using custom renderer
I tried below event but it is firing in both cases when child page being pushed or tab being changed. how can i get isolated event of tab being changed
public class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
this.CurrentPageChanged += CurrentPageHasChanged;
}
protected void CurrentPageHasChanged(object sender,EventArgs e)
{
var pages= Navigation.NavigationStack;
if (pages.Count > 0)
{
this.Title = pages[pages.Count - 1].Title;
}
else
this.Title = this.CurrentPage.Title;
}
}
This issue I am facing is: In below screenshot part1 is Homepage(title="Diary") & part2 is Childpage(title="Homework") when I change tab & again come to first tab than navigationbar title getting changed "Homework" to "Diary"(Screeshot2)
As you are on your tabbed page already you can literally just do the following
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
CurrentPageChanged += CurrentPageHasChanged;
}
private void CurrentPageHasChanged(object sender, EventArgs e) => Title = CurrentPage.Title;
}
if you want to use the sender you can do the following
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
CurrentPageChanged += CurrentPageHasChanged;
}
private void CurrentPageHasChanged(object sender, EventArgs e)
{
var tabbedPage = (TabbedPage) sender;
Title = tabbedPage.CurrentPage.Title;
}
}
Or if you can elaborate on what you are trying to do exactly I can give a better answer as I do not know what it is that you are trying to do exactly
I don't think you can achieve what you want to do, at least not like this. The event behaves the way it does and as described: it is fired whenever the current page changes, also for children.
That being said, I think you should focus on implementing the functionality you want with the tools we have. I can't really deduce from your code what you are trying to do, but it looks like you want to change the title? When a tab is changed? Why not just make some kind of condition to only do it for certain pages? For example when the page is contained in the Children collection?

Bind button content to a variable

I have the following in XAML:
<Button Content="{Binding KB.Text}" />
KB is an instance of the following class:
public class ButtonText
{
public string Text
{
get
{
return "Button";
}
}
}
I have KB defined as global variable in the code behind of the page, the button content is showing empty when running the project, how would I achieve this? The button content should be retrieved by KB.Text
Make sure that your "KB" object is initialized (not null).
You might have missed "this.DataContext = this" in your main function
Make sure your KB is property
This works for me:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
KB = new ButtonText();
}
public ButtonText KB { get; }
}
public class ButtonText
{
public string Text
{
get
{
return "Button";
}
}
}
EDIT:
I wrote the first solution having in mind WPF, took me a while to figure there's a "UWP" tag.
In UWP, if you want to bind something to the code behind of the designer itself (*xaml.cs), you should use "x:Bind" instead of "Binding".
See the link about x:Bind vs Binding
In short, your xaml should look like so:
<Button Content="{x:Bind KB.Text}"/>

Init some static resources in codebehind and have access to them in XAML (WPF)

I have a big image that I load to memory once ,split this image to multiple BitmapImage and from this point on there will be no changes to those images.
With this part i'm done: Initializing this list right after InitializeComponent(); of the Window partial class..
The only problem is , I cannot access to this list from my XAML file. I have tired so many different ways described online but none worked.
I am asking this question as a general issue and not a specific error I get cause there might be a better way to achieve the same purpose using C#/wpf mechanisms that i'm not aware of.. (i'm mainly doing java)
Thanks!
EDIT1: (after implementing #Janne Matikainen solution)
Now I get this error (not positive its related)
where the Uri for the image I use is :
new Uri(#"pack://application:,,,/Images/myimage.bmp", UriKind.Absolute)
and the folder Images lays in the project root.
Is it related to our issue?
EDIT 2 :
The warning for the URI is just a warning - It basically says it cannot find the URI since the link to the bitmaps will be built in runtime , and it cannot "find" it during compile time..
You should not create resource dynamically. If you do, it has to be done before you call InitializeCompontent() and you need to access the resource using {DynamicResource ResourceKey} markup extension.
You can use Binding with RelativeSource to access properties defined in code behind:
<ItemsControl ItemsSource="{Binding MyBitmapSources, RelativeSource={RelativeSource AncestorType=local:MainWindow}}" />
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources { get; private set; }
public MainWindow()
{
MyBitmapSources = new ObservableCollection<BitmapImage>();
InitializeComponent();
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
//set uri, or streamsource here
bitmapImage.EndInit();
MyBitmapSources.Add(bitmapImage);
}
}
However, if you use binding, you should initialize the properties before you call InilializeComponent(). Otherwise you need to notify UI that the property has changed. Or in our case, that the collection has changed. I have used ObservableCollection, because it notifies UI about changes automatically.
EDIT:
If you want to share the BitmapImage between multiple views, just use singleton pattern or similar:
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources
{
get { return MySingleton.Instance.MyBitmapSources; }
}
or you can store the collection in application resources:
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources
{
get { return (ObservableCollection<BitmapImage>)Application.Current.Resources["MyBitmapSources"]; }
set { Application.Current.Resources["MyBitmapSources"] = value; }
}
First you need a static view model that you can bind from within your view(s). Then you load the image and split it within the private constructor and expose the Image or collection, depending on the binding approach you take.
public class StaticImageViewModel
{
private static readonly Lazy<StaticImageViewModel> Lazy = new Lazy<StaticImageViewModel>(() => new StaticImageViewModel());
private List<BitmapImage> images;
private StaticImageViewModel()
{
this.images = new List<BitmapImage>
{
new BitmapImage(new Uri("pack://application:,,,/WpfApplication1;component/Images/Image1.png"))
};
}
public static StaticImageViewModel Instance
{
get { return Lazy.Value; }
}
public BitmapImage Image
{
get
{
return this.images[0];
}
}
}
And then you can bind from this static viewmodel like this, you just need to make a valueconverter or such to get image with specific name/index from your dictionary/list.
<Image Source="{Binding Source={x:Static local:StaticImageViewModel.Instance}, Path=Image}"></Image>

WPF layout and specific "data-binding"

I'm writing a WPF 95% GUI application, and need a specific shortcut-icon behaviour.
One image is sometimes better than (lots of) words, so here's the basic layout:
(as a new user, couldn't attach an image...)
http://imageshack.us/f/705/appb.jpg/
-Every icons in the bottom sub menu represents a control "page" in the app - FIXED icons.
-The icons on the left are shortcuts - recent and favourites - according to clicks on the bottom icons.
-I'm using stack-panels as containers, and the number of items is fixed.
-every control set has it's own context-menu.
What I'm trying to accomplish is as follows:
When the user clicks an item in the bottom menu, I want it to "magically appear" in the "Recent" panel. When user clicks "remove" (context menu) at the "Recent" panel, I need the (right-clicked) icon to go away.
Right now my (working) solution is unbelievably cumbersome, and I'm sure an elegant one exists..
Would very much appreciate any advice,
Daniel.
I assume you're using the MVVM pattern for this? If not, you should be.
So, assuming you're using MVVM, would something like this work for you:
class BottomPanelViewModel
{
public BottomPanelViewModel()
{
Items = new ObservableCollection<PageViewModel>();
ItemsView = new ListCollectionView(Items);
ItemsView.CurrentChanged += SelectionChanged;
}
public ObservableCollection<PageViewModel> Items { get; private set; }
public ListCollectionView ItemsView { get; private set; }
}
class RecentPanelViewModel
{
public RecentPanelViewModel()
{
Items = new ObservableCollection<PageViewModel>();
}
public ObservableCollection<PageViewModel> Items { get; private set; }
}
class WindowViewModel
{
public WindowViewModel()
{
BottomPanel = new BottomPanelViewModel();
RecentPanel = new RecentPanelViewModel();
BottomPanel.CurrentChanged += (s, e) =>
{
RecentPanel.Items.Add(BottomPanel.ItemsView.CurrentItem);
};
}
public BottomPanelViewModel BottomPanel { get; private set; }
public RecentPanelViewModel RecentPanel { get; private set; }
}
In your window constructor, create a WindowViewModel instance and use it as your DataContext:
public Window()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
Then in your XAML you can bind to the properties of the WindowViewModel:
<Window ...>
<DockPanel>
<ListBox DockPanel.Dock="Bottom"
ItemsSource="{Binding BottomPanel.ItemsView}"
IsSynchronizedWithCurrentItem="True"/>
<ListBox DockPanel.Dock="Left"
ItemsSource="{Binding RecentPanel.Items}"/>
</DockPanel>
</Window>
Explanation: the WindowViewModel contains a BottomPanelViewModel and a RecentPanelViewModel. Each contain an ObservableCollection of Items, and the bottom panel also exposes a collection view. The collection view allows us to track the current selection in the UI.
I'm using simple ListBoxes in the example XAML, but you can use whatever ItemsControl you like.
When the selection changes in the bottom panel, the window view model hears this and adds the selected item to the recent panel's ObservableCollection. You'll obviously want to add logic here to check for duplicates etc.

WPF OpenFileDialog with the MVVM pattern? [duplicate]

This question already has answers here:
Open File Dialog MVVM
(7 answers)
Closed 2 years ago.
I just started learning the MVVM pattern for WPF. I hit a wall: what do you do when you need to show an OpenFileDialog?
Here's an example UI I'm trying to use it on:
When the browse button is clicked, an OpenFileDialog should be shown. When the user selects a file from the OpenFileDialog, the file path should be displayed in the textbox.
How can I do this with MVVM?
Update: How can I do this with MVVM and make it unit test-able? The solution below doesn't work for unit testing.
What I generally do is create an interface for an application service that performs this function. In my examples I'll assume you are using something like the MVVM Toolkit or similar thing (so I can get a base ViewModel and a RelayCommand).
Here's an example of an extremely simple interface for doing basic IO operations like OpenFileDialog and OpenFile. I'm showing them both here so you don't think I'm suggesting you create one interface with one method to get around this problem.
public interface IOService
{
string OpenFileDialog(string defaultPath);
//Other similar untestable IO operations
Stream OpenFile(string path);
}
In your application, you would provide a default implementation of this service. Here is how you would consume it.
public MyViewModel : ViewModel
{
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set { _selectedPath = value; OnPropertyChanged("SelectedPath"); }
}
private RelayCommand _openCommand;
public RelayCommand OpenCommand
{
//You know the drill.
...
}
private IOService _ioService;
public MyViewModel(IOService ioService)
{
_ioService = ioService;
OpenCommand = new RelayCommand(OpenFile);
}
private void OpenFile()
{
SelectedPath = _ioService.OpenFileDialog(#"c:\Where\My\File\Usually\Is.txt");
if(SelectedPath == null)
{
SelectedPath = string.Empty;
}
}
}
So that's pretty simple. Now for the last part: testability. This one should be obvious, but I'll show you how to make a simple test for this. I use Moq for stubbing, but you can use whatever you'd like of course.
[Test]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
Mock<IOService> ioServiceStub = new Mock<IOService>();
//We use null to indicate invalid path in our implementation
ioServiceStub.Setup(ioServ => ioServ.OpenFileDialog(It.IsAny<string>()))
.Returns(null);
//Setup target and test
MyViewModel target = new MyViewModel(ioServiceStub.Object);
target.OpenCommand.Execute();
Assert.IsEqual(string.Empty, target.SelectedPath);
}
This will probably work for you.
There is a library out on CodePlex called "SystemWrapper" (http://systemwrapper.codeplex.com) that might save you from having to do a lot of this kind of thing. It looks like FileDialog is not supported yet, so you'll definitely have to write an interface for that one.
Edit:
I seem to remember you favoring TypeMock Isolator for your faking framework. Here's the same test using Isolator:
[Test]
[Isolated]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
IOService ioServiceStub = Isolate.Fake.Instance<IOService>();
//Setup stub arrangements
Isolate.WhenCalled(() => ioServiceStub.OpenFileDialog("blah"))
.WasCalledWithAnyArguments()
.WillReturn(null);
//Setup target and test
MyViewModel target = new MyViewModel(ioServiceStub);
target.OpenCommand.Execute();
Assert.IsEqual(string.Empty, target.SelectedPath);
}
The WPF Application Framework (WAF) provides an implementation for the Open and SaveFileDialog.
The Writer sample application shows how to use them and how the code can be unit tested.
Firstly I would recommend you to start off with a WPF MVVM toolkit. This gives you a nice selection of Commands to use for your projects. One particular feature that has been made famous since the MVVM pattern's introduction is the RelayCommand (there are manny other versions of course, but I just stick to the most commonly used). Its an implementation of the ICommand interface that allows you to crate a new command in your ViewModel.
Back to your question,here is an example of what your ViewModel may look like.
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public OpenFileDialogVM()
{
RegisterCommands();
}
public OpenFileDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
dialog.ShowDialog();
SelectedPath = dialog.FileName;
}
}
ViewModelBase and RelayCommand are both from the MVVM Toolkit. Here is what the XAML may look like.
<TextBox Text="{Binding SelectedPath}" />
<Button Command="vm:OpenFileDialogVM.OpenCommand" >Browse</Button>
and your XAML.CS code behind.
DataContext = new OpenFileDialogVM();
InitializeComponent();
Thats it.
As you get more familiar with the commands, you can also set conditions as to when you want the Browse button to be disabled, etc. I hope that pointed you in the direction you wanted.
In my opinion the best solution is creating a custom control.
The custom control I usually create is composed from:
Textbox or textblock
Button with an image as template
String dependency property where the file path will be wrapped to
So the *.xaml file would be like this
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Button Grid.Column="1" Click="Button_Click">
<Button.Template>
<ControlTemplate>
<Image Grid.Column="1" Source="../Images/carpeta.png"/>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
And the *.cs file:
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
typeof(string),
typeof(customFilePicker),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal));
public string Text
{
get
{
return this.GetValue(TextProperty) as String;
}
set
{
this.SetValue(TextProperty, value);
}
}
public FilePicker()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if(openFileDialog.ShowDialog() == true)
{
this.Text = openFileDialog.FileName;
}
}
At the end you can bind it to your view model:
<controls:customFilePicker Text="{Binding Text}"/>
From my perspective the best option is the prism library and InteractionRequests. The action to open the dialog remains within the xaml and gets triggered from Viewmodel while the Viewmodel does not need to know anything about the view.
See also
https://plainionist.github.io///Mvvm-Dialogs/
As example see:
https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/PopupCommonDialogAction.cs
https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/InteractionRequest/OpenFileDialogNotification.cs

Categories

Resources