[C#] Windows Forms 응용 프로그램에서 응용 프로그램 설정을 저장하려면 어떻게해야합니까?

달성하고자하는 것은 매우 간단합니다. 정보를 읽는 경로를 사용하는 Windows Forms (.NET 3.5) 응용 프로그램이 있습니다. 이 경로는 내가 제공 한 옵션 양식을 사용하여 사용자가 수정할 수 있습니다.

이제 나중에 사용할 수 있도록 경로 값을 파일에 저장하려고합니다. 이것은이 파일에 저장된 많은 설정 중 하나입니다. 이 파일은 응용 프로그램 폴더에 직접 저장됩니다.

세 가지 옵션을 사용할 수 있음을 이해합니다.

  • ConfigurationSettings 파일 (appname.exe.config)
  • 기재
  • 사용자 정의 XML 파일

.NET 구성 파일이 값을 다시 저장할 것으로 예상되지 않는다는 것을 읽었습니다. 레지스트리에 관해서는 가능한 한 멀리 떨어지고 싶습니다.

이것은 사용자 정의 XML 파일을 사용하여 구성 설정을 저장해야 함을 의미합니까?

그렇다면 해당 코드 예제 (C #)를보고 싶습니다.

이 주제에 대한 다른 토론을 보았지만 여전히 명확하지 않습니다.



답변

Visual Studio로 작업하면 지속 가능한 설정을 얻는 것이 매우 쉽습니다. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 속성을 선택하십시오. 설정이 존재하지 않으면 설정 탭을 선택하고 하이퍼 링크를 클릭하십시오.

설정 탭을 사용하여 응용 프로그램 설정을 만듭니다. Visual Studio에서 파일을 생성 Settings.settings하고 Settings.Designer.settings그 싱글 톤 클래스를 포함 Settings로부터 상속 ApplicationSettingsBase을 . 코드에서이 클래스에 액세스하여 응용 프로그램 설정을 읽고 쓸 수 있습니다.

Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file

이 기술은 콘솔, Windows Forms 및 기타 프로젝트 유형 모두에 적용 할 수 있습니다.

설정의 범위 속성 을 설정해야합니다 . 응용 프로그램 범위를 선택하면 설정. 기본. <사용자 속성>이 읽기 전용이됩니다.

참조 : 방법 : C #을 사용하여 런타임시 사용자 설정 작성 -Microsoft Docs


답변

실행 파일과 동일한 디렉토리에 파일로 저장하려는 경우 JSON 형식 을 사용하는 훌륭한 솔루션이 있습니다 .

using System;
using System.IO;
using System.Web.Script.Serialization;

namespace MiscConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            MySettings settings = MySettings.Load();
            Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
            Console.WriteLine("Incrementing 'myInteger'...");
            settings.myInteger++;
            Console.WriteLine("Saving settings...");
            settings.Save();
            Console.WriteLine("Done.");
            Console.ReadKey();
        }

        class MySettings : AppSettings<MySettings>
        {
            public string myString = "Hello World";
            public int myInteger = 1;
        }
    }

    public class AppSettings<T> where T : new()
    {
        private const string DEFAULT_FILENAME = "settings.json";

        public void Save(string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
        }

        public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
        }

        public static T Load(string fileName = DEFAULT_FILENAME)
        {
            T t = new T();
            if(File.Exists(fileName))
                t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
            return t;
        }
    }
}


답변

레지스트리는 끝이 없습니다. 응용 프로그램을 사용하는 사용자에게 레지스트리에 쓸 수있는 충분한 권한이 있는지 확실하지 않습니다.

app.config파일을 사용하여 응용 프로그램 레벨 설정 (응용 프로그램을 사용하는 각 사용자에 대해 동일)을 저장할 수 있습니다 .

사용자 별 설정을 XML 파일에 저장하면 격리 된 저장소 또는 SpecialFolder.ApplicationData 디렉터리 에 저장됩니다 .

그 옆에 .NET 2.0에서와 같이 값을 app.config파일에 다시 저장할 수 있습니다.


답변

ApplicationSettings클래스에 설정을 저장을 지원하지 않습니다 의 app.config 파일. 그것은 의도적으로 설계된 것입니다. 올바르게 보안 된 사용자 계정으로 실행되는 응용 프로그램 (Vista UAC 생각)은 프로그램의 설치 폴더에 대한 쓰기 권한이 없습니다.

당신은 ConfigurationManager클래스 와 시스템을 싸울 수 있습니다 . 그러나 간단한 해결 방법은 설정 디자이너로 이동하여 설정 범위를 사용자로 변경하는 것입니다. 이로 인해 어려움이 발생하는 경우 (즉, 설정은 모든 사용자와 관련이 있습니다) 옵션 기능을 별도의 프로그램에 넣어서 권한 상승 프롬프트를 요청할 수 있습니다. 또는 설정을 사용하여 포기하십시오.


답변

registry / configurationSettings / XML 인수는 여전히 매우 활성화 된 것으로 보입니다. 기술이 발전함에 따라 나는 그것들을 모두 사용했지만, 내가 가장 좋아하는 것은 Isolated Storage 와 결합 된 Threed의 시스템을 기반으로합니다. 합니다.

다음 샘플에서는 properties라는 객체를 격리 된 저장소의 파일에 저장할 수 있습니다. 같은 :

AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");

다음을 사용하여 속성을 복구 할 수 있습니다.

AppSettings.Load(myobject, "myFile.jsn");

모범 사례를 암시하는 것이 아니라 샘플 일뿐입니다.

internal static class AppSettings
{
    internal static void Save(object src, string targ, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = src.GetType();

        string[] paramList = targ.Split(new char[] { ',' });
        foreach (string paramName in paramList)
            items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify.
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write((new JavaScriptSerializer()).Serialize(items));
            }

        }
        catch (Exception) { }   // If fails - just don't use preferences
    }

    internal static void Load(object tar, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = tar.GetType();

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
            using (StreamReader reader = new StreamReader(stream))
            {
                items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
            }
        }
        catch (Exception) { return; }   // If fails - just don't use preferences.

        foreach (KeyValuePair<string, object> obj in items)
        {
            try
            {
                tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
            }
            catch (Exception) { }
        }
    }
}


답변

내가 만든 라이브러리를 공유하고 싶었습니다. 작은 라이브러리이지만 .settings 파일보다 큰 개선 (IMHO)입니다.

라이브러리를 Jot (GitHub) 라고 합니다. 여기에 내가 쓴 코드 프로젝트 기사 가 있습니다.

창의 크기와 위치를 추적하는 데 사용하는 방법은 다음과 같습니다.

public MainWindow()
{
    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();
}

.settings 파일과 비교할 때의 이점 : 코드가 상당히 적으며 각 속성을 한 번만 언급하면되므로 오류가 훨씬 적습니다 .

설정 파일을 사용하면 각 속성을 5 번 언급해야합니다. 속성 을 명시 적으로 만들 때 한 번, 코드에서 값을 앞뒤로 복사하는 추가 4 번 을 언급해야 합니다.

스토리지, 직렬화 등을 완전히 구성 할 수 있습니다. 대상 객체가 IoC에 의해 생성 된 경우 컨테이너에 [후킹] []하여 해결하는 모든 객체에 자동으로 추적을 적용하여 속성을 영구적으로 만들기 위해 수행해야하는 모든 작업이 [추적 가능] 그것에 속성.

고도로 구성 가능하며 다음과 같이 구성 할 수 있습니다. 속성에 대한 데이터 유지

나를 믿어 라, 도서관은 최고 수준이다!


답변

간단한 방법은 구성 데이터 개체를 사용하여 로컬 폴더에 응용 프로그램 이름으로 XML 파일로 저장 한 다음 시작할 때 다시 읽는 것입니다.

다음은 폼의 위치와 크기를 저장하는 예입니다.

구성 데이터 객체는 강력하게 형식화되어 있으며 사용하기 쉽습니다.

[Serializable()]
public class CConfigDO
{
    private System.Drawing.Point m_oStartPos;
    private System.Drawing.Size m_oStartSize;

    public System.Drawing.Point StartPos
    {
        get { return m_oStartPos; }
        set { m_oStartPos = value; }
    }

    public System.Drawing.Size StartSize
    {
        get { return m_oStartSize; }
        set { m_oStartSize = value; }
    }
}

저장 및로드를위한 관리자 클래스 :

public class CConfigMng
{
    private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
    private CConfigDO m_oConfig = new CConfigDO();

    public CConfigDO Config
    {
        get { return m_oConfig; }
        set { m_oConfig = value; }
    }

    // Load configuration file
    public void LoadConfig()
    {
        if (System.IO.File.Exists(m_sConfigFileName))
        {
            System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
            Type tType = m_oConfig.GetType();
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            object oData = xsSerializer.Deserialize(srReader);
            m_oConfig = (CConfigDO)oData;
            srReader.Close();
        }
    }

    // Save configuration file
    public void SaveConfig()
    {
        System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
        Type tType = m_oConfig.GetType();
        if (tType.IsSerializable)
        {
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            xsSerializer.Serialize(swWriter, m_oConfig);
            swWriter.Close();
        }
    }
}

이제 인스턴스를 만들고 폼의로드 및 닫기 이벤트에서 사용할 수 있습니다.

    private CConfigMng oConfigMng = new CConfigMng();

    private void Form1_Load(object sender, EventArgs e)
    {
        // Load configuration
        oConfigMng.LoadConfig();
        if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
        {
            Location = oConfigMng.Config.StartPos;
            Size = oConfigMng.Config.StartSize;
        }
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Save configuration
        oConfigMng.Config.StartPos = Location;
        oConfigMng.Config.StartSize = Size;
        oConfigMng.SaveConfig();
    }

그리고 생성 된 XML 파일도 읽을 수 있습니다 :

<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <StartPos>
    <X>70</X>
    <Y>278</Y>
  </StartPos>
  <StartSize>
    <Width>253</Width>
    <Height>229</Height>
  </StartSize>
</CConfigDO>