簡體   English   中英

在頁面之間傳遞文件 - UWP C#

[英]Pass file between pages - UWP C#

我想知道在 UWP 應用程序的頁面之間傳遞文件的最佳方法是什么?

我有一個包含兩頁的 UWP 應用程序。 在第一頁中,我讓用戶使用文件選擇器打開一個文件並將該文件加載到媒體播放器中。

當用戶導航到第二頁時,我想將相同的文件傳遞到第二頁。 我當前將文件作為字符串傳遞,然后嘗試使用 GetFileFromPathAsync 將其作為存儲文件加載。

這目前有效,因為我能夠在第二頁上加載文件,但它要求用戶提供廣泛的文件系統訪問權限。

第 1 頁上的代碼(FileLoaded 是文件路徑字符串):

private async void TranscodeMedia_Click(object sender, RoutedEventArgs e)
        {
            AppWindow appWindow = await AppWindow.TryCreateAsync();
            Frame appWindowContentFrame = new Frame();
            appWindowContentFrame.Navigate(typeof(TranscodeMedia), FileLoaded);

第 2 頁上的代碼:

    protected override async void OnNavigatedTo(NavigationEventArgs e)
    {
        var fileTransfer = e.Parameter.ToString();

        FileName.Text = fileTransfer;

        StorageFile PassedFile = await StorageFile.GetFileFromPathAsync(fileTransfer);

我想知道這是否是在頁面之間傳遞文件的最佳方式? 如果可能,我寧願不要求用戶提供對應用程序的廣泛系統訪問權限。 非常感謝您提供的任何幫助!

還有一些其他方法可以實現您在不同頁面上訪問同一文件的要求。 但對於您的場景,您可以在 UWP 應用程序中使用未來訪問列表

通過選擇文件和文件夾,您的用戶授予您的應用程序訪問可能無法訪問的項目的權限。 如果您將這些項目添加到您的未來訪問列表中,那么當您的應用稍后想要再次訪問這些項目時,您將保留該權限。

這是我制作的示例代碼

在第一頁:

        FileOpenPicker picker = new FileOpenPicker();

        picker.FileTypeFilter.Add("*");

        StorageFile file = await picker.PickSingleFileAsync();

        if (file != null) 
        {
            // add file to the Future Access list
            var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
            // this token is the key to get the file.
            string FALToken = storageItemAccessList.Add(file, "mediaFile");
            // in your real scenario, you need to save the token and pass it when you nee
            this.Frame.Navigate(typeof(TestPage), FALToken);
        }

在第二頁:

   protected override async void OnNavigatedTo(NavigationEventArgs e)
    {
        string token = (string)e.Parameter;

        var storageItemAccessList = StorageApplicationPermissions.FutureAccessList;

        StorageFile retrievedFile = await storageItemAccessList.GetFileAsync(token);


    }

因此,如果您使用Future-access list來保留文件的權限,則不需要廣泛的文件系統訪問權限。

有關更多詳細信息,請參閱此文檔: 跟蹤最近使用的文件和文件夾

C#/WPF/UWP 方式中最好和最標准的方法是使用由通用 ViewModel class 組成的標准模式(其中包含您要在邏輯層中使用的所有常見應用程序數據),作為字段放入static MainPage(甚至在 App.xaml.cs 類中)。

我總是這樣做:

1)我使用自動創建的 MainPage 作為應用程序的“外殼”,其屬性是AppViewModel MainPage (以及AppViewModel )可以從應用程序的任何地方訪問,方法是將自身設置為自己的 class 中的static字段(“當前”ZA81259CEF8E959C624DF1D456E5D327 中的消息字段甚至可以在應用程序中的任何地方調用... class。)。

這是 MainPage 的代碼(或您希望的 shell 頁面,但我建議這樣做,這甚至是 Microsoft 使用的一種非常標准的方式),比您想象的要簡單:

public sealed partial class MainPage : Page
{
    public AppViewModel ViewModel { get; set; } = new AppViewModel();
    public static MainPage Current { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        Current = this;
    }
}

這就是訣竅:在其自己的 class 的一個字段中制作頁面static ,以便 static 字段在整個應用程序中是唯一的“靜態詞”,因此是“靜態詞”的主要功能之一調用MainPage.Current.ViewModel您可以立即獲取存儲在那里的任何數據(在您的特定情況下為 StorageFile)。

2) AppViewModel本身是一個 class 必須實現INotifyPropertyChanged接口,以啟用可綁定的屬性和功能。 在 Windows 開發人員中,通常會創建一個實現它的基礎 class,然后從中派生所有需要可綁定(即可觀察)屬性的類。

這就是微軟自己創建它的方式:

public class BaseBind : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    protected bool SetProperty<T>(ref T storage, T value,
        [CallerMemberName] String propertyName = null)
    {
        if (object.Equals(storage, value)) return false;
        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

然后,您從中派生AppViewModel class(以及所有其他 model 和 viewmodel 類)……使用您需要跨頁面共享的所有常見屬性填充它。 我什至添加了一個派生屬性,以展示如何一次共享多個數據類型,以及一個 function:

public class AppViewModel : BaseBind
{
    public AppViewModel()
    {
        // Usually we initialize all the starting data here, in the viewmodel constructor...
    }

    // All common app data
    private string sampleCommonString;
    public String SampleCommonString
    {
        get { return sampleCommonString; }
        set { SetProperty(ref sampleCommonString, value); OnPropertyChanged(nameof(SampleDerivedProperty1)); OnPropertyChanged(nameof(SampleDerivedProperty2)); }
    }

    public String SampleDerivedProperty1 =>  "return something based on SampleCommonString";

    public String SampleDerivedProperty2
    {
        get
        {
            // evaluate in some way SampleCommonString...

            return "Same thing as SampleDerivedProperty1, but it allows to add more than just one istruction";
        }
    }

    // This is a property that you can use for functions and internal logic… but it CAN'T be binded to the UI directly
    public String SampleNOTBindableProperty { get; set; }

    public void SampleFunction()
    {
        // Insert code, that needs to interact with all the data contained in the viewmodel itself, here...

        // The function has to be with NO parameters, in order to work with simple {x:Bind} markup.
        // If your function has to access some specific data, you can create a new bindable (or non) property, just as the ones above, and memorize the data there.
    }
}

3)然后,為了從另一個Page訪問所有這些,只需在該頁面中創建一個AppViewModel字段,引用 static 主頁中包含的視圖模型:

public sealed partial class SecondPage : Page
{
    public AppViewModel ViewModel => MainPage.Current.ViewModel;

    public SecondPage()
    {
        this.InitializeComponent();
    }
}

...並且您可以輕松地將 XAML 控件屬性綁定到AppViewModel本身:

<TextBlock Text="{x:Bind ViewModel.SampleCommonString, Mode=OneWay}"/>
<TextBox Text="{x:Bind ViewModel.SampleCommonString, Mode=TwoWay}"/>
<Button Content="Sample content" Click="{x:Bind ViewModel.SampleFunction}"/>

Mode=OneWay用於實時綁定,以便即使在 UI 中屬性也會立即更新,而Mode=TwoWay用於那些可以由用戶從控件本身編輯的屬性,以便進行交互與應用程序邏輯)。

在這種模式下,您將能夠實時顯示數據及其所有變化!

所以......這是以正確和靈活的方式在運行時保持所有應用程序數據的方法......通過學習和練習,將來您將通過創建視圖模型以更智能的方式使用這種模式對於您的應用程序的每個object (例如:如果您的應用程序需要存儲您公司的客戶數據,您將有一個“CustomerViewModel”class 派生自 BaseBind class 並創建所有ObservableCollection<SampleViewModel>客戶數據的列表, ObservableCollection<SampleViewModel>來存儲所有這些( ObservableCollection<t>是一種集合類型,它具有處理列表更改的內置機制,例如添加、刪除和重新排序列表項)。 然后,您將每個可觀察的集合鏈接到從 ListBase class (通常: ListViewGridView )繼承的控件的ItemsSource屬性,創建一個 DataTemplate 來顯示每個列表項,如下例所示:

<Page
    xmlns:vm="using:SampleApp.ViewModelsPath"

    <Grid>
    <ListView ItemsSource="{x:Bind ViewModel.SampleListOfObjectViewModel, Mode=OneWay}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="vm:SampleObjectViewModel">
                <StackPanel>
                    <TextBlock Text="{x:Bind SampleObjectProperty1, Mode=OneWay}"/>
                    <TextBlock Text="{x:Bind SampleObjectProperty2, Mode=OneWay}"/>
                    <Button Click="{x:Bind SampleObjectFunction}"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>
</Page>

...並且顯示的所有數據將在您更改時實時更新!

希望這一切都能幫助您提高有關如何准備 WPF/UWP邏輯層的知識,因為即使對於 WPF 應用程序(即舊桌面程序),所有這些都以相同的方式工作。

此致

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM